【发布时间】:2020-10-29 00:10:40
【问题描述】:
我们制作了一个当前使用 WebView 全屏显示网页内容的 Android 应用。
这可行,但性能很大程度上取决于 WebVeiw 组件的版本,并且 Chrome 浏览器更新时并不总是更新(在不同的 Android 版本中,WebView 组件和 Chrome 浏览器之间的关系相对复杂) .从谷歌关于该主题的演示中,我们得出结论,使用 TWA 我们可能会获得更好和更一致的性能,因为 TWA 功能与 Chrome 浏览器一起更新。因此,我们希望在 TWA 不存在时使用 TWA 并回退到 WebView(我们的应用程序在 Android 4.4 和更高版本上运行)。
应用程序需要执行更多的逻辑,而不仅仅是显示 Web 内容,因此我们不能只在 Manifest 中定义 TWA/WebView。检查是否能够使用 TWA 并在 MainActivity.java 中启动 TWA 或回退到 WebView 已实现。但是,使用 TWA 时,URL/地址栏和底部导航栏仍然可见。
网址/地址栏: 据我们所知,要使 TWA 不显示 URL/地址栏,TWA 中显示的域必须有一个“匹配”Android 应用程序证书的 /.well-known/assetlinks.json 文件。包含有关此信息和有用链接的两个页面是 https://developers.google.com/web/android/trusted-web-activity/integration-guide 和 https://developer.android.com/training/app-links/verify-site-associations。使用https://developers.google.com/digital-asset-links/tools/generator 创建assetlinks.json 并使用https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=website_url&relation=delegate_permission/common.handle_all_urls 成功检查
在虚拟设备(Android API 级别 29,Chrome v83)上测试期间,我们启用了 Chrome 的“在非 root 设备上启用命令行”标志,并在终端中启用了
$ adb shell "echo '_ --disable-digital-asset-link-verification-for-url=\"website_url"' > /data/local/tmp/chrome-command-line"
之后,Chrome 会显示一条警告消息,但 URL/地址栏仍然存在。
底部导航栏: Chrome v80 和更新版本应该支持使用沉浸式选项移除底部导航栏:https://bugs.chromium.org/p/chromium/issues/detail?id=965329#c18 尽管使用所描述的用于创建全屏应用程序的选项 (https://developer.android.com/training/system-ui/immersive#java),底部导航栏仍然显示。
我们如何去除 URL/地址栏和底部导航栏,基本上我们如何让 TWA 中的网页内容全屏显示?
我们查看了以下示例应用,以了解我们需要做些什么才能使 TWA 正常工作,但没有发现任何工作(尽管我们错过了一些重要的东西并非不可想象):
- https://github.com/GoogleChromeLabs/svgomg-twa
- https://github.com/tsuyosh/TrustedWebActivitiesDemo
- https://github.com/GoogleChrome/android-browser-helper
- https://github.com/elliatab/TwaDemo
- https://github.com/thanhtungka91/TwaDemoJava
我们项目文件的相关内容:
Manifest.json
<application
android:name="main_application"
android:hardwareAccelerated="true"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:banner="@drawable/banner"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/AppTheme" >
<activity
android:name="main_activity"
android:hardwareAccelerated="true"
android:label="@string/app_name"
android:launchMode = "singleInstance"
android:keepScreenOn="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
MainActivity.java
public class MainActivity extends Activity implements IServiceCallbacks {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set up looks of the view
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
@Override
public void onSystemUiVisibilityChange(int visibility) {
// Note that system bars will only be "visible" if none of the
// LOW_PROFILE, HIDE_NAVIGATION, or FULLSCREEN flags are set.
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
// bars are visible => user touched the screen, make the bars disappear again in 2 seconds
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
hideBars();
}
}, 2000);
} else {
// The system bars are NOT visible => do nothing
}
}
});
decorView.setKeepScreenOn(true);
setContentView(R.layout.activity_main);
// create Trusted Web Access or fall back to a WebView
String chromePackage = CustomTabsClient.getPackageName(this, TrustedWebUtils.SUPPORTED_CHROME_PACKAGES, true);
if (chromePackage != null) {
if (!chromeVersionChecked) {
TrustedWebUtils.promptForChromeUpdateIfNeeded(this, chromePackage);
chromeVersionChecked = true;
}
if (savedInstanceState != null && savedInstanceState.getBoolean(MainActivity.TWA_WAS_LAUNCHED_KEY)) {
this.finish();
} else {
this.twaServiceConnection = new MainActivity.TwaCustomTabsServiceConnection();
CustomTabsClient.bindCustomTabsService(this, chromePackage, this.twaServiceConnection);
}
} else {
// set up WebView
}
}
private class TwaCustomTabsServiceConnection extends CustomTabsServiceConnection {
public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient client) {
CustomTabsSession session = MainActivity.this.getSession(client);
CustomTabsIntent intent = MainActivity.this.getCustomTabsIntent(session);
Uri url = Uri.parse("http://our_url");
TrustedWebUtils.launchAsTrustedWebActivity(MainActivity.this, intent, url);
MainActivity.this.twaWasLaunched = true;
}
public void onServiceDisconnected(ComponentName componentName) {
}
}
protected void hideBars() {
if (getWindow() != null) {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
// Hide the nav bar and status bar
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
// Set the content to appear under the system bars so that the
// content doesn't resize when the system bars hide and show.
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
// Enables regular immersive mode
| View.SYSTEM_UI_FLAG_IMMERSIVE
);
}
// Remember that you should never show the action bar if the
// status bar is hidden, so hide that too if necessary.
ActionBar actionBar = getActionBar();
if (actionBar != null) {
actionBar.hide();
}
}
}
build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
allprojects {
repositories {
jcenter()
google()
maven { url "https://jitpack.io" }
}
}
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '29.0.2'
defaultConfig {
applicationId application_id
minSdkVersion 19
targetSdkVersion 29
}
buildTypes {
release {
minifyEnabled true
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
jniDebuggable true
renderscriptDebuggable true
renderscriptOptimLevel 3
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.leanback:leanback:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.webkit:webkit:1.2.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.github.GoogleChrome.custom-tabs-client:customtabs:master'
}
/.well-known/assetlinks.json
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "our package name",
"sha256_cert_fingerprints": ["11:22:33:44"]
}
},
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target" : { "namespace": "android_app", "package_name": "our other package name",
"sha256_cert_fingerprints": ["11:22:33:44"]
}
}
]
【问题讨论】:
-
我在实现 TWA 时遇到的一件事是,在我们的例子中,显示模式应该由 PWA 设置,而不是由 Android TWA 应用程序设置。检查你的 PWA 的 web manifest,确保显示模式设置为你想要的模式,即
fullscreen。有关显示模式文档,请参阅 developer.mozilla.org/en-US/docs/Web/Manifest/display。请参阅 superpwa.com/doc/web-app-manifest-display-modes 以更好地了解通过 TWA 显示时每种模式的外观。 -
你找到解决办法了吗?
标签: java android trusted-web-activity twa