Skip to content

Commit 8e40770

Browse files
Protocol Handler support
1 parent 65e5fc1 commit 8e40770

File tree

3 files changed

+96
-21
lines changed

3 files changed

+96
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.google.androidbrowserhelper.trusted;
2+
3+
import android.net.Uri;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
7+
8+
/**
9+
* Storage container for URLs that will be passed to the browser within a
10+
* {@link androidx.browser.trusted.TrustedWebActivityIntent}.
11+
*/
12+
public class LaunchUrlBundle {
13+
14+
private @NonNull final Uri resolvedUrl;
15+
private @Nullable final Uri originalUrl;
16+
17+
public LaunchUrlBundle(@NonNull Uri resolvedUrl, @Nullable Uri originalUrl) {
18+
this.resolvedUrl = resolvedUrl;
19+
this.originalUrl = originalUrl;
20+
}
21+
22+
/**
23+
* This is the target URL that will be opened by the browser in a TWA context.
24+
* It is always present here.
25+
*/
26+
public @NonNull Uri getResolvedUrl() {
27+
return resolvedUrl;
28+
}
29+
30+
/**
31+
* The URL that was originally invoked before being processed and converted into the resolved
32+
* URL. Currently the only use case for this is Protocol Handlers, which convert a URL with a
33+
* custom data scheme into a http/https location. If the URL was never processed and the
34+
* resolved URL is the same as the original, this returns null.
35+
*/
36+
public @Nullable Uri getOriginalUrl() {
37+
return originalUrl;
38+
}
39+
}

androidbrowserhelper/src/main/java/com/google/androidbrowserhelper/trusted/LauncherActivity.java

+51-15
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838

3939
import org.json.JSONException;
4040

41+
import java.util.Collections;
42+
import java.util.Map;
43+
import java.util.Objects;
44+
4145
/**
4246
* A convenience class to make using Trusted Web Activities easier. You can extend this class for
4347
* basic modifications to the behaviour.
@@ -200,8 +204,9 @@ protected void launchTwa() {
200204
getColorCompat(mMetadata.navigationBarDividerColorDarkId))
201205
.build();
202206

207+
LaunchUrlBundle launchUrlBundle = getLaunchUrlBundle();
203208
TrustedWebActivityIntentBuilder twaBuilder =
204-
new TrustedWebActivityIntentBuilder(getLaunchingUrl())
209+
new TrustedWebActivityIntentBuilder(launchUrlBundle.getResolvedUrl())
205210
.setToolbarColor(getColorCompat(mMetadata.statusBarColorId))
206211
.setNavigationBarColor(getColorCompat(mMetadata.navigationBarColorId))
207212
.setNavigationBarDividerColor(
@@ -212,6 +217,9 @@ protected void launchTwa() {
212217
.setDisplayMode(getDisplayMode())
213218
.setScreenOrientation(mMetadata.screenOrientation);
214219

220+
// TODO When available
221+
// twaBuilder.setOriginalLaunchUrl(launchUrlBundle.getOriginalUrl());
222+
215223
if (mMetadata.additionalTrustedOrigins != null) {
216224
twaBuilder.setAdditionalTrustedOrigins(mMetadata.additionalTrustedOrigins);
217225
}
@@ -339,26 +347,54 @@ public void onEnterAnimationComplete() {
339347
}
340348

341349
/**
342-
* Returns the URL that the Trusted Web Activity should be launched to. By default this
343-
* implementation checks to see if the Activity was launched with an Intent with data, if so
344-
* attempt to launch to that URL. If not, read the
350+
* Override this to enable Protocol Handler support.
351+
* Keys of this map are data schemes, e.g. "bitcoin", "irc", "xmpp", "web+coffee", and values
352+
* are templates that will be used to construct the full URL. The template must contain a "%s"
353+
* token and be an absolute location with http/https scheme and the same origin as the TWA.
354+
*
355+
* An example valid entry in the map would be:
356+
* ["web+coffee"] -> ["https://coffee.com/?type=%s"]
357+
* This would result in a link "web+coffee://latte" being converted to
358+
* "https://coffee.com/?type=latte".
359+
*
360+
* {@see https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Manifest/Reference/protocol_handlers}
361+
*/
362+
protected Map<String, Uri> getProtocolHandlers() {
363+
return Collections.emptyMap();
364+
}
365+
366+
/**
367+
* Returns the {@link LaunchUrlBundle} that the Trusted Web Activity should be launched to.
368+
* By default this implementation starts by checking if the Activity was launched with an Intent
369+
* with data. If so, check if that URL is a Protocol Handler custom scheme and if so, convert
370+
* it to an appropriate http/https location (keeping the original custom scheme URL in the
371+
* bundle). Otherwise, read the
345372
* "android.support.customtabs.trusted.DEFAULT_URL" metadata from the manifest.
346373
*
347374
* Override this for special handling (such as ignoring or sanitising data from the Intent).
348375
*/
349-
protected Uri getLaunchingUrl() {
350-
Uri uri = getIntent().getData();
351-
if (uri != null) {
352-
Log.d(TAG, "Using URL from Intent (" + uri + ").");
353-
return uri;
354-
}
355-
356-
if (mMetadata.defaultUrl != null) {
357-
Log.d(TAG, "Using URL from Manifest (" + mMetadata.defaultUrl + ").");
358-
return Uri.parse(mMetadata.defaultUrl);
376+
protected LaunchUrlBundle getLaunchUrlBundle() {
377+
Uri baseUrl = Uri.parse(mMetadata.defaultUrl);
378+
379+
Uri intentUrl = getIntent().getData();
380+
381+
if (intentUrl != null) {
382+
Map<String, Uri> protocolHandlers = getProtocolHandlers();
383+
String scheme = intentUrl.getScheme();
384+
if (protocolHandlers.containsKey(scheme)) {
385+
String format = Objects.requireNonNull(protocolHandlers.get(scheme)).toString();
386+
String target = intentUrl.toString().replace(scheme + "://", "");
387+
Uri targetUrl = Uri.parse(String.format(format, target));
388+
Log.d(TAG, "Using protocol handler url: " + targetUrl);
389+
return new LaunchUrlBundle(targetUrl, intentUrl);
390+
}
391+
392+
Log.d(TAG, "Using url from Intent: " + intentUrl);
393+
return new LaunchUrlBundle(intentUrl, null);
359394
}
360395

361-
return Uri.parse("https://www.example.com/");
396+
Log.d(TAG, "Using url from Manifest: " + baseUrl);
397+
return new LaunchUrlBundle(baseUrl, null);
362398
}
363399

364400
/**

demos/twa-firebase-analytics/src/main/java/com/google/androidbrowserhelper/demos/twa_firebase_analytics/FirebaseAnalyticsLauncherActivity.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,9 @@
1717
import android.net.Uri;
1818
import android.os.Bundle;
1919

20-
import androidx.annotation.NonNull;
2120
import androidx.annotation.Nullable;
2221

23-
import com.google.android.gms.tasks.OnCompleteListener;
24-
import com.google.android.gms.tasks.Task;
22+
import com.google.androidbrowserhelper.trusted.LaunchUrlBundle;
2523
import com.google.androidbrowserhelper.trusted.LauncherActivity;
2624
import com.google.firebase.analytics.FirebaseAnalytics;
2725

@@ -60,12 +58,14 @@ protected boolean shouldLaunchImmediately() {
6058
}
6159

6260
@Override
63-
protected Uri getLaunchingUrl() {
64-
Uri uri = super.getLaunchingUrl();
61+
protected LaunchUrlBundle getLaunchUrlBundle() {
62+
LaunchUrlBundle bundle = super.getLaunchUrlBundle();
6563
// Attach the Firebase instance Id to the launchUrl. This example uses "appInstanceId" as
6664
// the parameter name.
67-
return uri.buildUpon()
65+
Uri resolvedUrl = bundle.getResolvedUrl().buildUpon()
6866
.appendQueryParameter("appInstanceId", mAppInstanceId)
6967
.build();
68+
69+
return new LaunchUrlBundle(resolvedUrl, bundle.getOriginalUrl());
7070
}
7171
}

0 commit comments

Comments
 (0)