Skip to content

Instantly share code, notes, and snippets.

@KleversonNascimento
Created May 6, 2019 05:11
Show Gist options
  • Save KleversonNascimento/3ca0bf3173f04d3215e7289784666421 to your computer and use it in GitHub Desktop.
Save KleversonNascimento/3ca0bf3173f04d3215e7289784666421 to your computer and use it in GitHub Desktop.
LauncherActivity for TWA
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package br.com.quintoandar.inquilinos;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.customtabs.CustomTabsCallback;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsService;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import android.support.customtabs.TrustedWebUtils;
import android.support.customtabs.trusted.TrustedWebActivityService;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A convenience class to make using Trusted Web Activities easier. You can extend this class for
* basic modifications to the behaviour.
*
* If you just want to wrap a website in a Trusted Web Activity you should:
* 1) Copy the manifest for the svgomg project.
* 2) Set up Digital Asset Links [1] for your site and app.
* 3) Set the DEFAULT_URL metadata in the manifest and the browsable intent filter to point to your
* website.
*
* You can skip (2) if you just want to try out TWAs but not on your own website, but you must
* add the {@code --disable-digital-asset-link-verification-for-url=https://svgomg.firebaseapp.com}
* to Chrome for this to work [2].
*
* You may also go beyond this and add notification delegation, which causes push notifications to
* be shown by your app instead of Chrome. This is detailed in the javadoc for
* {@link TrustedWebActivityService}.
*
* If you just want default behaviour your Trusted Web Activity client app doesn't even need any
* Java code - you just set everything up in the Android Manifest!
*
* At the moment this only works with Chrome Dev, Beta and local builds (launch progress [3]).
*
* [1] https://developers.google.com/digital-asset-links/v1/getting-started
* [2] https://www.chromium.org/developers/how-tos/run-chromium-with-flags#TOC-Setting-Flags-for-Chrome-on-Android
* [3] https://www.chromestatus.com/feature/4857483210260480
*/
public class LauncherActivity extends AppCompatActivity {
private static final String TAG = "LauncherActivity";
private static final String METADATA_DEFAULT_URL =
"android.support.customtabs.trusted.DEFAULT_URL";
private static final String METADATA_STATUS_BAR_COLOR =
"android.support.customtabs.trusted.STATUS_BAR_COLOR";
private static final String TWA_WAS_LAUNCHED_KEY =
"android.support.customtabs.trusted.TWA_WAS_LAUNCHED_KEY";
private static final String STABLE_PACKAGE = "com.android.chrome";
private static final String BETA_PACKAGE = "com.chrome.beta";
private static final String DEV_PACKAGE = "com.chrome.dev";
private static final int SESSION_ID = 96375;
private static final String FAKE_URI = "https://www.google.com";
@Nullable private TwaCustomTabsServiceConnection mServiceConnection;
@Nullable private String mDefaultUrl;
private int mStatusBarColor;
private boolean mTwaWasLaunched;
private String mCustomTabsProviderPackage;
/** We only want to show the update prompt once per instance of this application. */
private static boolean sChromeVersionChecked;
/**
* Connects to the CustomTabsService.
*/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Changes init here
if(isChromeInstalledAndVersionGreaterThan72()) {//TWA is supported?
//TWA is supported!
mCustomTabsProviderPackage = CustomTabsClient.getPackageName(this,
TrustedWebUtils.SUPPORTED_CHROME_PACKAGES, true);
} else {
String packageNameCustomTabs = getPackageNameCustomTabs(this);
if(packageNameCustomTabs != null) {//Custom Tabs is supported?
//Custom Tabs is supported!
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
intentBuilder.enableUrlBarHiding();
intentBuilder.setToolbarColor(getResources().getColor(R.color.colorAccent));
CustomTabsIntent customTabsIntent = intentBuilder.build();
customTabsIntent.intent.setPackage(packageNameCustomTabs);
customTabsIntent.launchUrl(this, getLaunchingUrl());
} else {
//TWA and Custom Tabs not supported
Uri fakeUri = Uri.parse(FAKE_URI);
Intent shareIntent = new Intent(Intent.ACTION_VIEW, fakeUri);
List<ResolveInfo> resInfo;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
resInfo = getPackageManager().queryIntentActivities(
shareIntent, PackageManager.MATCH_ALL);
} else {
resInfo = getPackageManager().queryIntentActivities(
shareIntent, 0);
}
if(!resInfo.isEmpty()) {
String packageNameBrowser = resInfo.get(0).activityInfo.packageName;
Intent intent = new Intent(Intent.ACTION_VIEW, getLaunchingUrl());
intent.setPackage(packageNameBrowser);
startActivity(intent);
}
}
finish();
}
//Changes finish here
if (mCustomTabsProviderPackage == null) {
finish();
return;
}
if (!sChromeVersionChecked) {
sChromeVersionChecked = true;
}
if (savedInstanceState != null && savedInstanceState.getBoolean(TWA_WAS_LAUNCHED_KEY)) {
// This activity died in the background after launching Trusted Web Activity, then
// the user closed the Trusted Web Activity and ended up here.
finish();
return;
}
parseMetadata();
mServiceConnection = new TwaCustomTabsServiceConnection();
CustomTabsClient.bindCustomTabsService(
this, mCustomTabsProviderPackage, mServiceConnection);
}
private void parseMetadata() {
try {
Bundle metaData = getPackageManager().getActivityInfo(
new ComponentName(this, getClass()), PackageManager.GET_META_DATA).metaData;
if (metaData == null) {
return;
}
mDefaultUrl = metaData.getString(METADATA_DEFAULT_URL);
mStatusBarColor = ContextCompat.getColor(
this, metaData.getInt(METADATA_STATUS_BAR_COLOR, android.R.color.white));
} catch (PackageManager.NameNotFoundException e) {
// Will only happen if the package provided (the one we are running in) is not
// installed - so should never happen.
}
}
@Override
protected void onRestart() {
super.onRestart();
if (mTwaWasLaunched) {
finish(); // The user closed the Trusted Web Activity and ended up here.
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mServiceConnection != null) {
unbindService(mServiceConnection);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(TWA_WAS_LAUNCHED_KEY, mTwaWasLaunched);
}
/**
* Creates a {@link CustomTabsSession}. Default implementation returns a CustomTabsSession using
* a constant session id, see {@link CustomTabsClient#newSession(CustomTabsCallback, int)}, so
* that if an instance of Trusted Web Activity associated with this app is already running, the
* new Intent will be routed to it, allowing for seamless page transitions. The user will be
* able to navigate to the previous page with the back button.
*
* Override this if you want any special session specific behaviour. To launch separate Trusted
* Web Activity instances, return CustomTabsSessions either without session ids (see
* {@link CustomTabsClient#newSession(CustomTabsCallback)}) or with different ones on each
* call.
*/
protected CustomTabsSession getSession(CustomTabsClient client) {
return client.newSession(null, SESSION_ID);
}
/**
* Creates a {@link CustomTabsIntent} to launch the Trusted Web Activity.
* By default, Trusted Web Activity will be launched in the same Android Task.
* Override this if you want any special launching behaviour.
*/
protected CustomTabsIntent getCustomTabsIntent(CustomTabsSession session) {
return new CustomTabsIntent.Builder(session)
.setToolbarColor(mStatusBarColor)
.build();
}
/**
* Returns the URL that the Trusted Web Activity should be launched to. By default this
* implementation checks to see if the Activity was launched with an Intent with data, if so
* attempt to launch to that URL. If not, read the
* "android.support.customtabs.trusted.DEFAULT_URL" metadata from the manifest.
*
* Override this for special handling (such as ignoring or sanitising data from the Intent).
*/
protected Uri getLaunchingUrl() {
Uri uri = getIntent().getData();
if (uri != null) {
Log.d(TAG, "Using URL from Intent (" + uri + ").");
return uri;
}
if (mDefaultUrl != null) {
Log.d(TAG, "Using URL from Manifest (" + mDefaultUrl + ").");
return Uri.parse(mDefaultUrl);
}
return Uri.parse("https://www.example.com");
}
private class TwaCustomTabsServiceConnection extends CustomTabsServiceConnection {
@Override
public void onCustomTabsServiceConnected(ComponentName componentName,
CustomTabsClient client) {
if (TrustedWebUtils.warmupIsRequired(
LauncherActivity.this, mCustomTabsProviderPackage)) {
client.warmup(0);
}
CustomTabsSession session = getSession(client);
CustomTabsIntent intent = getCustomTabsIntent(session);
Uri url = getLaunchingUrl();
Log.d(TAG, "Launching Trusted Web Activity.");
TrustedWebUtils.launchAsTrustedWebActivity(LauncherActivity.this,
intent,
url);
// Remember who we connect to as the package that is allowed to delegate notifications
// to us.
TrustedWebActivityService.setVerifiedProvider(
LauncherActivity.this, mCustomTabsProviderPackage);
mTwaWasLaunched = true;
}
@Override
public void onServiceDisconnected(ComponentName componentName) { }
}
private void finishAndRemoveTaskCompat() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
finishAndRemoveTask();
} else {
finish();
}
}
private String getPackageNameCustomTabs(Context context) {
String mPackageNameToUse = null;
// Get default VIEW intent handler that can view a web url.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(FAKE_URI));
// Get all apps that can handle VIEW intents.
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
mPackageNameToUse = null;
} else {
mPackageNameToUse = packagesSupportingCustomTabs.get(0);
}
if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
mPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
mPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
mPackageNameToUse = DEV_PACKAGE;
}
return mPackageNameToUse;
}
private boolean isChromeInstalledAndVersionGreaterThan72() {
PackageInfo pInfo;
try {
pInfo = getPackageManager().getPackageInfo(STABLE_PACKAGE, 0);
} catch (PackageManager.NameNotFoundException e) {
//chrome is not installed on the device
return false;
}
if (pInfo != null) {
//Chrome has versions like 68.0.3440.91, we need to find the major version
//using the first dot we find in the string
int firstDotIndex = pInfo.versionName.indexOf(".");
//take only the number before the first dot excluding the dot itself
String majorVersion = pInfo.versionName.substring(0, firstDotIndex);
return Integer.parseInt(majorVersion) >= 72;
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment