Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io,app: add deeplinking support #117

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/GioActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import android.app.Activity;
import android.os.Bundle;
import android.content.Intent;
import android.content.res.Configuration;
import android.view.ViewGroup;
import android.view.View;
Expand All @@ -29,6 +30,7 @@ public final class GioActivity extends Activity {

layer.addView(view);
setContentView(layer);
onNewIntent(this.getIntent());
}

@Override public void onDestroy() {
Expand Down Expand Up @@ -60,4 +62,9 @@ public final class GioActivity extends Activity {
if (!view.backPressed())
super.onBackPressed();
}

@Override protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
view.onIntentEvent(intent);
}
}
11 changes: 11 additions & 0 deletions app/GioView.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
Expand Down Expand Up @@ -311,6 +312,15 @@ private void setHighRefreshRate() {
window.setAttributes(layoutParams);
}

protected void onIntentEvent(Intent intent) {
inkeliz marked this conversation as resolved.
Show resolved Hide resolved
if (intent == null) {
return;
}
if (intent.getData() != null) {
this.onOpenURI(nhandle, intent.getData().toString());
}
}

@Override protected boolean dispatchHoverEvent(MotionEvent event) {
if (!accessManager.isTouchExplorationEnabled()) {
return super.dispatchHoverEvent(event);
Expand Down Expand Up @@ -549,6 +559,7 @@ void updateCaret(float m00, float m01, float m02, float m10, float m11, float m1
static private native void onExitTouchExploration(long handle);
static private native void onA11yFocus(long handle, int viewId);
static private native void onClearA11yFocus(long handle, int viewId);
static private native void onOpenURI(long handle, String uri);
static private native void imeSetSnippet(long handle, int start, int end);
static private native String imeSnippet(long handle);
static private native int imeSnippetStart(long handle);
Expand Down
1 change: 1 addition & 0 deletions app/framework_ios.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
#include <UIKit/UIKit.h>

@interface GioViewController : UIViewController
- (BOOL)onOpenURI:(NSString *)url;
@end
25 changes: 25 additions & 0 deletions app/internal/windows/windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ type MonitorInfo struct {
Flags uint32
}

type CopyDataStruct struct {
DwData uintptr
CbData uint32
LpData uintptr
}

const (
TRUE = 1

Expand Down Expand Up @@ -247,6 +253,7 @@ const (
WM_CANCELMODE = 0x001F
WM_CHAR = 0x0102
WM_CLOSE = 0x0010
WM_COPYDATA = 0x004A
WM_CREATE = 0x0001
WM_DPICHANGED = 0x02E0
WM_DESTROY = 0x0002
Expand Down Expand Up @@ -344,6 +351,7 @@ var (
_DefWindowProc = user32.NewProc("DefWindowProcW")
_DestroyWindow = user32.NewProc("DestroyWindow")
_DispatchMessage = user32.NewProc("DispatchMessageW")
_FindWindow = user32.NewProc("FindWindowW")
_EmptyClipboard = user32.NewProc("EmptyClipboard")
_GetWindowRect = user32.NewProc("GetWindowRect")
_GetClientRect = user32.NewProc("GetClientRect")
Expand Down Expand Up @@ -374,6 +382,7 @@ var (
_ReleaseDC = user32.NewProc("ReleaseDC")
_ScreenToClient = user32.NewProc("ScreenToClient")
_ShowWindow = user32.NewProc("ShowWindow")
_SendMessage = user32.NewProc("SendMessageW")
_SetCapture = user32.NewProc("SetCapture")
_SetCursor = user32.NewProc("SetCursor")
_SetClipboardData = user32.NewProc("SetClipboardData")
Expand Down Expand Up @@ -473,6 +482,14 @@ func EmptyClipboard() error {
return nil
}

func FindWindow(lpClassName string) (syscall.Handle, error) {
hwnd, _, err := _FindWindow.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpClassName))), 0)
if hwnd == 0 {
return 0, fmt.Errorf("FindWindow failed: %v", err)
}
return syscall.Handle(hwnd), nil
}

func GetWindowRect(hwnd syscall.Handle) Rect {
var r Rect
_GetWindowRect.Call(uintptr(hwnd), uintptr(unsafe.Pointer(&r)))
Expand Down Expand Up @@ -780,6 +797,14 @@ func ReleaseDC(hdc syscall.Handle) {
_ReleaseDC.Call(uintptr(hdc))
}

func SendMessage(hwnd syscall.Handle, msg uint32, wParam, lParam uintptr) error {
r, _, err := _SendMessage.Call(uintptr(hwnd), uintptr(msg), wParam, lParam)
if r == 0 {
return fmt.Errorf("SendMessage failed: %v", err)
}
return nil
}

func SetForegroundWindow(hwnd syscall.Handle) {
_SetForegroundWindow.Call(uintptr(hwnd))
}
Expand Down
13 changes: 12 additions & 1 deletion app/os_android.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,12 @@ import "C"
import (
"errors"
"fmt"
"gioui.org/io/transfer"
"image"
"image/color"
"io"
"math"
"net/url"
"os"
"path/filepath"
"runtime"
Expand All @@ -146,7 +148,6 @@ import (
"gioui.org/io/pointer"
"gioui.org/io/semantic"
"gioui.org/io/system"
"gioui.org/io/transfer"
"gioui.org/unit"
)

Expand Down Expand Up @@ -667,6 +668,16 @@ func Java_org_gioui_GioView_onClearA11yFocus(env *C.JNIEnv, class C.jclass, view
}
}

//export Java_org_gioui_GioView_onOpenURI
func Java_org_gioui_GioView_onOpenURI(env *C.JNIEnv, class C.jclass, view C.jlong, uri C.jstring) {
w := cgo.Handle(view).Value().(*window)
u, err := url.Parse(goString(env, uri))
if err != nil {
return
}
w.ProcessEvent(transfer.URLEvent{URL: u})
}

func (w *window) ProcessEvent(e event.Event) {
w.processEvent(e)
}
Expand Down
34 changes: 34 additions & 0 deletions app/os_ios.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import "C"
import (
"image"
"io"
"net/url"
"os"
"runtime"
"runtime/cgo"
Expand Down Expand Up @@ -121,6 +122,13 @@ type window struct {

var mainWindow = newWindowRendezvous()

// activeViews is the list of active views.
var activeViews = make([]*window, 0, 1)

// startupURI is the URI to open when the first window is created,
// but no window is active yet.
var startupURI *url.URL

func init() {
// Darwin requires UI operations happen on the main thread only.
runtime.LockOSThread()
Expand All @@ -145,8 +153,13 @@ func onCreate(view, controller C.CFTypeRef) {
}
w.displayLink = dl
C.gio_viewSetHandle(view, C.uintptr_t(cgo.NewHandle(w)))
activeViews = append(activeViews, w)
w.Configure(wopts.options)
w.ProcessEvent(UIKitViewEvent{ViewController: uintptr(controller)})
if startupURI != nil {
w.ProcessEvent(transfer.URLEvent{URL: startupURI})
startupURI = nil
}
}

func viewFor(h C.uintptr_t) *window {
Expand Down Expand Up @@ -213,6 +226,12 @@ func onDestroy(h C.uintptr_t) {
w.displayLink.Close()
w.displayLink = nil
cgo.Handle(h).Delete()
for i, v := range activeViews {
if v == w {
activeViews = append(activeViews[:i], activeViews[i+1:]...)
break
}
}
w.view = 0
}

Expand Down Expand Up @@ -425,6 +444,21 @@ func osMain() {
}
}

//export gio_onOpenURI
func gio_onOpenURI(uri C.CFTypeRef) {
u, err := url.Parse(nsstringToString(uri))
if err != nil {
return
}
if len(activeViews) == 0 {
startupURI = u
return
}
for _, w := range activeViews {
w.ProcessEvent(transfer.URLEvent{URL: u})
}
}

//export gio_runMain
func gio_runMain() {
if !isMainThread() {
Expand Down
13 changes: 11 additions & 2 deletions app/os_ios.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ - (void)keyboardWillHide:(NSNotification *)note {
_keyboardHeight = 0.0;
[self.view setNeedsLayout];
}

- (BOOL)onOpenURI:(NSString *)url {
gio_onOpenURI((__bridge CFTypeRef)url);
return YES;
}
#endif
@end

Expand Down Expand Up @@ -283,16 +288,20 @@ void gio_viewSetHandle(CFTypeRef viewRef, uintptr_t handle) {

@interface _gioAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) GioViewController *controller;
@end

@implementation _gioAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
GioViewController *controller = [[GioViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = controller;
self.controller = [[GioViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = self.controller;
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
return [self.controller onOpenURI:url.absoluteString];
}
@end

int gio_applicationMain(int argc, char *argv[]) {
Expand Down
37 changes: 37 additions & 0 deletions app/os_macos.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"errors"
"image"
"io"
"net/url"
"runtime"
"runtime/cgo"
"strings"
Expand Down Expand Up @@ -334,6 +335,14 @@ type window struct {
// launched is closed when applicationDidFinishLaunching is called.
var launched = make(chan struct{})

// activeViews is the list of active windows.
var activeViews = make([]*window, 0, 1)

// startupURI is the URL event that was received before the app was launched.
// Since the app is not running yet, the URL event is stored and processed after
// the view is created.
var startupURI *url.URL

// nextTopLeft is the offset to use for the next window's call to
// cascadeTopLeftFromPoint.
var nextTopLeft C.NSPoint
Expand Down Expand Up @@ -865,6 +874,10 @@ func gio_onAttached(h C.uintptr_t, attached C.int) {
if attached != 0 {
layer := C.layerForView(w.view)
w.ProcessEvent(AppKitViewEvent{View: uintptr(w.view), Layer: uintptr(layer)})
if startupURI != nil {
w.ProcessEvent(transfer.URLEvent{URL: startupURI})
startupURI = nil
}
} else {
w.ProcessEvent(AppKitViewEvent{})
w.visible = false
Expand All @@ -879,6 +892,12 @@ func gio_onDestroy(h C.uintptr_t) {
w.displayLink.Close()
w.displayLink = nil
cgo.Handle(h).Delete()
for i, win := range activeViews {
if win == w {
activeViews = append(activeViews[:i], activeViews[i+1:]...)
break
}
}
w.view = 0
}

Expand Down Expand Up @@ -914,6 +933,23 @@ func gio_onFinishLaunching() {
close(launched)
}

//export gio_onOpenURI
func gio_onOpenURI(uri C.CFTypeRef) {
u, err := url.Parse(nsstringToString(uri))
if err != nil {
return
}

if len(activeViews) == 0 {
startupURI = u
return
}

for _, w := range activeViews {
w.ProcessEvent(transfer.URLEvent{URL: u})
}
}

func newWindow(win *callbacks, options []Option) {
<-launched
res := make(chan struct{})
Expand Down Expand Up @@ -971,6 +1007,7 @@ func (w *window) init() error {
return err
}
C.gio_viewSetHandle(view, C.uintptr_t(cgo.NewHandle(w)))
activeViews = append(activeViews, w)
w.view = view
return nil
}
Expand Down
6 changes: 6 additions & 0 deletions app/os_macos.m
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ CFTypeRef gio_createView(void) {
selector:@selector(applicationDidHide:)
name:NSApplicationDidHideNotification
object:nil];

return CFBridgingRetain(view);
}
}
Expand All @@ -412,6 +413,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSApp activateIgnoringOtherApps:YES];
gio_onFinishLaunching();
}
- (void)application:(NSApplication *)application openURLs:(NSArray<NSURL *> *)urls {
for (NSURL *url in urls) {
gio_onOpenURI((__bridge CFTypeRef)url.absoluteString);
}
}
@end

void gio_main() {
Expand Down
Loading