-
Notifications
You must be signed in to change notification settings - Fork 134
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
base: main
Are you sure you want to change the base?
Conversation
hey @inkeliz WOW this is a huge amount of work you have done. Thank you. Is there a code base with a basic demo we can use to test against ? I am guessing it is this ? https://github.com/gioui-plugins/gio-plugins/tree/add-deeplink and we also need gogio cmd ? |
I use this code for testing: https://gist.github.com/inkeliz/11c071ec4bf8f922a3cb48fd0cbb3b95. That is based on gio-plugins/deeplink/demo, but modified, since the events comes from |
Hey @inkeliz Ok i will try your gist. thanks. I got your deep link demo compiled off the correct git hashes. I am on mac and added your plist. panic(`deeplink: no schemes defined, you must use -ldflags "-X github.com/gioui-plugins/gio-plugins/deeplink.schemes=yourscheme,anotherscheme"`)
} |
@gedw99 That panic was from gio-plugins, not from gio (this current PR). You should not use gio-plugins/deeplink anymore, since everything was ported to Gio (this patch). I didn't use I park the code here, https://github.com/inkeliz/deeplink-demo. It uses the
That will listen to |
Just to remind me in the future:
|
Hi @inkeliz, thanks a lot for implementing this feature. |
In response to: #117 (comment) @inkeliz Yes your Demo code works on darwin_amd64. Really great work Desktop open ... Browser based open ... |
@mearaj, I don't think it's difficult to implement. However, personally, I don't use Linux, either my target-audience (it's < 1% that uses Linux). But, seems that you need to create a file, say
Then run However, its works similar to Windows: that will open the app passing the URL as the first parameter. So, it will require to create some IPC to send the URL to the active window, and make it a single-instance (either by pipe or d-bus). Currently, |
No worries, I understand. Thank you so much for the reply and providing the helpful info. :) |
HI @gedw99 |
If anyone is interested the Deeplinks, allows to then use Universal links. I made an issue here showing how to do it. |
Hi @ALL, |
I saw the gogio code, it doesn't supports linux as pointed out by @inkeliz. I have decided to create a separate repo for this feature on linux. It will support both gio and non gio apps. Will post the link here. Thanks :) |
looking forward to see it. Maybe later merge it into gio cmd if your want. |
Another alternative is to create some app.Scheme("yourscheme") option. Problems: that only works with Linux/Windows, because macOS/iOS/Android requires some manifest, and can't register it on runtime. So, what happens on iOS/Android?! Notice: My suggestion is NOT an My idea with Another approach, is to read Golang's AST. You use The easiest alternative: create a private-variable and then use Line 27 in d62057a
In this patch, a new one: Lines 1016 to 1018 in fe42c61
It not requires |
Hi @inkeliz , I mostly understood what you meant. I will try to implement it in gio itself by looking at the way you did for Windows. In case if I can't do it, I will share my implementation repo link here and will discuss with all of you guys :) |
Hi @inkeliz,@gedw99 and everyone. Desktop Entry Specs Before Window Creation:
The issue currently I am facing is that I don't know how to proceed further. In the current state whenever app receives any uri, it fires window.Event(transfer.URLEvent{URL: urlPtr}). I have created a PR at your branch.(It's not a final PR, but just for your review) |
discussion at gioui#117 (comment) Signed-off-by: Mearaj Bhagad <[email protected]>
On macOS (or iOS) it also receives multiple URLs, that is translated to multiple |
Thanks for asking.
These are the reasons I can currently think of :) |
Can you provide an example of how to open multiple URLs? Honestly, I've tried testing this using On Android, Currently, only macOS (and Linux) provide a list of URLs, and I'm not sure how to test that. Changing to |
ce5792f
to
9ef4718
Compare
Now, it's possible to launch one Gio app using a custom URI scheme, such as `gio://some/data`. This feature is supported on Android, iOS, macOS and Windows, issuing a new transfer.URLEvent, containing the URL launched. If the program is already open, one transfer.URLEvent will be sent to the current app. Limitations: On Windows, if the program listen to schemes (compiled with `-schemes`), then just a single instance of the app can be open. In other words, just a single `myprogram.exe` can be active. Security: Deeplinking have the same level of security of clipboard. Any other software can send such information and read the content, without any restriction. That should not be used to transfer sensible data, and can't be fully trusted. Setup/Compiling: In order to set the custom scheme, you need to use the new `-schemes` flag in `gogio`, using as `-schemes gio` will listen to `gio://`. If you are not using gogio you need to defined some values, which varies for each OS: macOS/iOS - You need to define the following Properly List: ``` <key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>yourCustomScheme</string> </array> </dict> </array> ``` Windows - You need to compiling using -X argument: ``` -ldflags="-X "gioui.org/app.schemesDeeplink=yourCustomScheme" -H=windowsgui" ``` Android - You need to add IntentFilter in GioActivity: ``` <intent-filter> <action android:name="android.intent.action.VIEW"></action> <category android:name="android.intent.category.DEFAULT"></category> <category android:name="android.intent.category.BROWSABLE"></category> <data android:scheme="yourCustomScheme"></data> </intent-filter> ``` That assumes that you still using GioActivity and GioAppDelegate, otherwise more changes are required. Signed-off-by: inkeliz <[email protected]>
@eliasnaur I need to squash multiple commits, due to rebase. I test have tested it on macOS, Android, iOS and Windows. I tried to avoid re-introducing the old |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great overall. A few nits and questions in the comments.
io/input/pointer.go
Outdated
@@ -320,6 +329,12 @@ func (p *pointerFilter) Matches(e event.Event) bool { | |||
return true | |||
} | |||
} | |||
case transfer.URLEvent: | |||
for _, t := range p.scheme { | |||
if e.URL != nil && (t == "" || t == e.URL.Scheme) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should assume e.URL != nil
.
case transfer.URLFilter: | ||
t = q // See comment in processEvent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not treat transfer.URLFilter
the same as the tag-less key.Filter
above, instead of inventing a fake tag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because q.key.scratchFilter = append(q.key.scratchFilter, f)
is a specific slice that holds key.Filter. Where the "generic one" (used by anything else) uses "taggedFilter". I don't think is right to add yet another specialised slice.
@@ -464,6 +468,10 @@ func (q *Router) processEvent(e event.Event, system bool) { | |||
cstate, evts := q.cqueue.Push(state.clipboardState, e) | |||
state.clipboardState = cstate | |||
q.changeState(e, state, evts) | |||
case transfer.URLEvent: | |||
var evts []taggedEvent | |||
evts = append(evts, taggedEvent{tag: q, event: e}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like key.Event
above, I would not expect a transfer.URLEvent
to have an associated tag.
// On Windows, launching the app using a URI will start a new instance of the app, | ||
// a new window. That behavior, by default, doesn't align with iOS/Android/macOS, where | ||
// the deeplink sends the event to the running app (if any). We are emulating it. | ||
if hwnd, _ := windows.FindWindow(ID); hwnd != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if multiple program instances have windows matching the ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is unlikely, and only one instance of the program must run anyway. Previously the executable path was used as identifier, then moved to appid
(#117 (comment)).
Maybe in gogio
it must throw one error if appid
is not provided and schemes
are.
Assuming that appid
is unique only one instance of this app will run.
Signed-off-by: inkeliz <[email protected]>
Signed-off-by: inkeliz <[email protected]>
Signed-off-by: inkeliz <[email protected]>
I wanted to merge this soon, and thought more about the corner-cases. I'm having cold feet with respect to exposing deeplinking support as
I don't have better ideas than to back out the package app
// Events is an iterator that yields events that are not specific to any window,
// such as [URLEvent]. It never returns.
//
// Events must be called by the main goroutine, and replaces the
// call to [Main].
func Events(yield func(event.Event) bool) and package app
func Main() {
Events(func(event.Event){})
} ( |
One consequence of a global |
Originally, the event was sent directly to window.Event (instead of gtx.Event). I find it odd to create another event handler (window.Event, layout.Context.Event, and now app.Events). While I understand the reasoning, since this Event is not associated with a specific Window, I think it's just too much. Also, some OSes (Android and Switch) it's somewhat bounded to the View/Window. |
f8029f2
to
026d3f9
Compare
How do you propose delivering the deeplink events to a running programs without open windows? |
3d36537
to
74ccc9c
Compare
Now, it's possible to launch one Gio app using a custom URI scheme, such as
gio://some/data
.This feature is supported on Android, iOS, macOS and Windows, issuing a new deeplink.Event,
containing the URL launched. If the program is already opened, one deeplink.Event will be
sent to the current opened app.
Limitations:
On Windows, if the program uses deeplink (compiled with
-deeplink
), then just a singleinstance of the app can be open. In other words, just a single
myprogram.exe
canbe active.
Security:
Deeplinking have the same level of security of clipboard. Any other software can send such
information and read the content, without any restriction. That should not be used to transfer
sensible data, and can't be fully trusted.
Setup/Compiling:
In order to set the custom scheme, you need to use the new
-deeplink
flag ingogio
, usingas
-deeplink gio
will listen togio://
.If you are not using gogio you need to defined some values, which varies for each OS:
macOS/iOS - You need to define the following Properly List:
Windows - You need to compiling using -X argument:
Android - You need to add IntentFilter in GioActivity:
That assumes that you still using GioActivity and GioAppDelegate, otherwise more
changes are required.