-
-
Notifications
You must be signed in to change notification settings - Fork 52
ADFA-5067: Support deep links to open projects and files #1651
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
base: stage
Are you sure you want to change the base?
Changes from all commits
2df5a56
6b96c84
8c42c35
0df3845
1109bf1
a0790b2
1078604
aea677b
3e8fd65
045aa00
ab4be5e
3ad035b
0f5b682
ee35586
4196a34
cc74e65
b68b50a
45d94cd
a451470
df705c9
de0e9e8
86c1f70
9741df7
e9a1afb
f8cb2c9
11d1988
a44feeb
6a92920
7e92715
fa73614
2a9c28a
3b7afd7
dbf4f55
8eb75ca
06751ad
df7d7b4
8343ea9
de62fac
de7ad7f
7a89bd6
cfb708e
44d0dbc
49c0cd3
8b8150f
232d249
17d29d1
e2e4f03
47a6eef
dd21d62
277435d
84bc0de
696fc4e
b8e1c43
656a236
3590380
85877e2
dcfeb90
40abc0a
b162f58
5528e14
cf4277c
8618ca6
44e4daa
6d9c8d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # `.well-known` (ADFA-5067) | ||
|
|
||
| `assetlinks.json` in this directory is the [RFC 5785](https://www.rfc-editor.org/rfc/rfc5785) / | ||
| [Digital Asset Links](https://developers.google.com/digital-asset-links) file required for Android | ||
| App Links to `https://www.appdevforall.org/device/open/project/...` to auto-verify. | ||
|
|
||
| This directory lives in the repo only until the actual website exists. To activate it: | ||
|
|
||
| 1. Copy this directory verbatim to the web server root, so it serves at | ||
| `https://www.appdevforall.org/.well-known/assetlinks.json` with `Content-Type: application/json`. | ||
| 2. Replace the `TODO_REPLACE_WITH_RELEASE_SIGNING_SHA256_FINGERPRINT` placeholder with the SHA-256 | ||
| fingerprint of the certificate that actually signs the released APK/AAB — get it via | ||
| `keytool -list -v -keystore <release.jks>` (whoever holds the release keystore), or from the Play | ||
| Console under **App integrity > App signing key certificate** if Play App Signing is used. This | ||
| cannot be filled in from source; it's a secret held by release engineering, not derivable from this | ||
| repository. | ||
|
|
||
| Until both steps are done, `android:autoVerify="true"` on `DeepLinkActivity`'s intent-filter will fail | ||
| Digital Asset Links verification, and Android may show a disambiguation chooser instead of opening the | ||
| app directly when a link is tapped. This is expected for now. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| [ | ||
| { | ||
| "relation": ["delegate_permission/common.handle_all_urls"], | ||
| "target": { | ||
| "namespace": "android_app", | ||
| "package_name": "com.itsaky.androidide", | ||
| "sha256_cert_fingerprints": [ | ||
| "TODO_REPLACE_WITH_RELEASE_SIGNING_SHA256_FINGERPRINT" | ||
|
davidschachterADFA marked this conversation as resolved.
|
||
| ] | ||
|
davidschachterADFA marked this conversation as resolved.
|
||
| } | ||
| } | ||
| ] | ||
|
Comment on lines
+1
to
+12
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove this entire file. It has been handled in #1693 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,22 @@ | |
| android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" | ||
| android:exported="true" | ||
| android:theme="@style/Theme.AndroidIDE" /> | ||
| <activity | ||
| android:name=".activities.DeepLinkActivity" | ||
| android:exported="true" | ||
| android:excludeFromRecents="true" | ||
| android:noHistory="true" | ||
| android:theme="@android:style/Theme.Translucent.NoTitleBar"> | ||
| <intent-filter android:autoVerify="true"> | ||
| <action android:name="android.intent.action.VIEW" /> | ||
| <category android:name="android.intent.category.DEFAULT" /> | ||
| <category android:name="android.intent.category.BROWSABLE" /> | ||
| <data | ||
| android:scheme="https" | ||
| android:host="www.appdevforall.org" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This declares Worth deciding rather than inheriting: the ticket's example URLs are all If you do want apex, it's free at this point — the server side already supports it. The published file uses <data
android:scheme="https"
android:host="appdevforall.org"
android:pathPrefix="/device/open/project/" />Note that adding it does mean |
||
| android:pathPrefix="/device/open/project/" /> | ||
| </intent-filter> | ||
| </activity> | ||
|
davidschachterADFA marked this conversation as resolved.
|
||
| <activity | ||
| android:name=".activities.editor.EditorActivityKt" | ||
| android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|fontScale" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /* | ||
| * This file is part of AndroidIDE. | ||
| * | ||
| * AndroidIDE is free software: you can redistribute it and/or modify | ||
| * it under the terms of the GNU General Public License as published by | ||
| * the Free Software Foundation, either version 3 of the License, or | ||
| * (at your option) any later version. | ||
| * | ||
| * AndroidIDE is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| * GNU General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU General Public License | ||
| * along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>. | ||
| */ | ||
|
|
||
| package com.itsaky.androidide.activities | ||
|
|
||
| import android.app.Activity | ||
| import android.content.Intent | ||
| import android.os.Bundle | ||
| import android.widget.Toast | ||
| import com.itsaky.androidide.activities.editor.EditorActivityKt | ||
| import com.itsaky.androidide.api.ActionContextProvider | ||
| import com.itsaky.androidide.models.DeepLinkRequest | ||
| import com.itsaky.androidide.resources.R.string | ||
|
|
||
| /** | ||
| * The sole `<intent-filter>` holder for `https://www.appdevforall.org/device/open/project/...` App | ||
| * Links. Never shows any UI -- it only parses the incoming [android.net.Uri], decides whether a | ||
| * project is already loaded, and hands off to whichever real activity owns that scenario: | ||
| * [MainActivity] if nothing is open yet, or the already-running [EditorActivityKt] (via its | ||
| * `singleTask` `onNewIntent`) if one is. | ||
| * | ||
| * Kept as a plain [Activity] (like [SplashActivity]), not [com.itsaky.androidide.app.BaseIDEActivity], | ||
| * since it never calls `setContentView` and has no theming needs of its own. | ||
| */ | ||
| class DeepLinkActivity : Activity() { | ||
| override fun onCreate(savedInstanceState: Bundle?) { | ||
| super.onCreate(savedInstanceState) | ||
|
|
||
| val request = DeepLinkRequest.parse(intent?.data) | ||
| if (request == null) { | ||
| // A Toast, not flashError -- this activity finishes immediately below, tearing down its | ||
| // window before a view-based Flashbar could ever render. | ||
| Toast.makeText(this, getString(string.msg_deeplink_invalid_link), Toast.LENGTH_LONG).show() | ||
| finish() | ||
| return | ||
| } | ||
|
|
||
| // ActionContextProvider tracks the live EditorHandlerActivity instance (set in its onCreate | ||
| // and re-asserted in onResume, cleared in onDestroy) -- this reflects "is an editor instance | ||
| // already alive to hand this off to via onNewIntent", unlike IProjectManager's workspace, | ||
| // which stays null for the whole duration of a Gradle sync even while EditorActivityKt is | ||
| // already open. | ||
| val target = | ||
|
davidschachterADFA marked this conversation as resolved.
|
||
| if (ActionContextProvider.getActivity() != null) { | ||
| EditorActivityKt::class.java | ||
| } else { | ||
| MainActivity::class.java | ||
| } | ||
|
|
||
| startActivity( | ||
| Intent(this, target).apply { | ||
| putExtra(DeepLinkRequest.EXTRA_KEY, request) | ||
| // If `target` is MainActivity and one already exists in the task, reuse it via | ||
| // onNewIntent instead of stacking a second instance -- SINGLE_TOP alone isn't enough | ||
| // here, since DeepLinkActivity (not MainActivity) is what's actually on top of the | ||
| // stack at this exact call, so SINGLE_TOP's "already at the top" check never matches; | ||
| // CLEAR_TOP finds MainActivity anywhere in the task and reuses it via onNewIntent | ||
| // (combined with SINGLE_TOP, rather than the destroy-and-recreate CLEAR_TOP alone | ||
| // would do). EditorActivityKt is singleTask, so it always reuses its live instance | ||
| // regardless of these flags. | ||
| addFlags( | ||
| Intent.FLAG_ACTIVITY_NEW_TASK or | ||
| Intent.FLAG_ACTIVITY_SINGLE_TOP or | ||
| Intent.FLAG_ACTIVITY_CLEAR_TOP, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CLEAR_TOP here can silently destroy unsaved work. This branch is taken when CLEAR_TOP then makes the framework finish that live editor. Its teardown runs Every unsaved buffer is dropped, with no prompt. |
||
| ) | ||
| }, | ||
| ) | ||
| finish() | ||
| } | ||
| } | ||
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.
Remove this entire file. It has been handled in #1693