Commit ce4e37e1 authored by Peter Kotwicz's avatar Peter Kotwicz Committed by Commit Bot

[Android WebAPK] Introduce APK which shows splash screen in WebAPK 3/X

This CL introduces NewSplashWebApk.apk The logic for displaying the
splash screen earlier will be implemented in NewSplashWebApk.apk
Once everything is ready we will switch the server over.

This CL also refactors WebappSplashScreenController in order to share code
with NewSplashWebApk.apk

Design doc:
https://docs.google.com/document/d/1ZvpZPAIxLl_ElyMWOkDybOhCFzYk0Di4WgpLVSJdEu8/edit?usp=sharing

BUG=817263

Change-Id: Id0561eede51fa7c2d293b321cfdcae755f1e03cf
Reviewed-on: https://chromium-review.googlesource.com/1201348
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarXi Han <hanxi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#592986}
parent 63451843
......@@ -109,6 +109,7 @@ android_resources("chrome_app_java_resources") {
}
deps = [
":chrome_strings_grd",
"//chrome/android/webapk/libs/common:splash_resources",
"//chrome/app:java_strings_grd",
"//components/autofill/android:autofill_java_resources",
"//components/policy:app_restrictions_resources",
......@@ -221,6 +222,7 @@ android_library("chrome_java") {
"//chrome/android/third_party/compositor_animator:compositor_animator_java",
"//chrome/android/webapk/libs/client:client_java",
"//chrome/android/webapk/libs/common:common_java",
"//chrome/android/webapk/libs/common:splash_java",
"//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java",
"//components/autofill/android:autofill_java",
"//components/background_task_scheduler:background_task_scheduler_java",
......
......@@ -36,10 +36,6 @@
<!-- Recent tabs page -->
<dimen name="recent_tabs_visible_separator_padding">28dp</dimen>
<!-- Web app dimensions -->
<dimen name="webapp_splash_offset">48dp</dimen>
<dimen name="webapp_splash_large_title_margin_bottom">8dp</dimen>
<!-- Payments UI -->
<dimen name="payments_ui_max_dialog_width">600dp</dimen>
......
......@@ -95,16 +95,6 @@
<item name="android:windowBackground">@null</item>
<item name="android:windowDisablePreview">true</item>
</style>
<style name="TextAppearance.WebappSplashScreenText" parent="BlackHeadline1">
<item name="android:textSize">24sp</item>
<item name="android:fontFamily">sans-serif-condensed</item>
</style>
<style name="WebappSplashScreenTextTheme">
<item name="android:ellipsize">end</item>
<item name="android:gravity">center</item>
<item name="android:maxLines">2</item>
<item name="android:textAppearance">@style/TextAppearance.WebappSplashScreenText</item>
</style>
<style name="AlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.Alert">
<item name="android:windowBackground">@drawable/popup_bg</item>
......
......@@ -162,7 +162,6 @@
<!-- WebappActivity colors -->
<color name="webapp_default_bg">@color/modern_grey_50</color>
<color name="webapp_splash_title_light">@android:color/white</color>
<!-- Bookmark UI colors -->
<color name="bookmark_detail_section">#7C7B79</color>
......
......@@ -249,15 +249,6 @@
icon was auto-generated by Chrome and whether the icon is bigger than a threshold. -->
<dimen name="webapp_home_screen_icon_size">48dp</dimen>
<dimen name="webapp_splash_offset">32dp</dimen>
<dimen name="webapp_splash_image_size_ideal">128dp</dimen>
<dimen name="webapp_splash_image_size_threshold">80dp</dimen>
<dimen name="webapp_splash_image_size_minimum">48dp</dimen>
<dimen name="webapp_splash_small_image_size">80dp</dimen>
<dimen name="webapp_splash_small_title_margin_top">32dp</dimen>
<dimen name="webapp_splash_large_title_margin_bottom">16dp</dimen>
<dimen name="webapk_badge_icon_size">24dp</dimen>
<!-- Toolbar dimensions -->
......
......@@ -29,6 +29,7 @@ import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.content_public.common.ScreenOrientationValues;
import org.chromium.webapk.lib.common.WebApkConstants;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import org.chromium.webapk.lib.common.WebApkMetaDataUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
......@@ -163,10 +164,11 @@ public class WebApkInfo extends WebappInfo {
IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.DISPLAY_MODE));
int orientation = orientationFromString(
IntentUtils.safeGetString(bundle, WebApkMetaDataKeys.ORIENTATION));
long themeColor = getLongFromMetaData(bundle, WebApkMetaDataKeys.THEME_COLOR,
ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
long backgroundColor = getLongFromMetaData(bundle, WebApkMetaDataKeys.BACKGROUND_COLOR,
ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
long themeColor = WebApkMetaDataUtils.getLongFromMetaData(bundle,
WebApkMetaDataKeys.THEME_COLOR, ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
long backgroundColor =
WebApkMetaDataUtils.getLongFromMetaData(bundle, WebApkMetaDataKeys.BACKGROUND_COLOR,
ShortcutHelper.MANIFEST_COLOR_INVALID_OR_MISSING);
int shellApkVersion =
IntentUtils.safeGetInt(bundle, WebApkMetaDataKeys.SHELL_APK_VERSION, 0);
......@@ -363,31 +365,6 @@ public class WebApkInfo extends WebappInfo {
}
}
/**
* Extracts long value from the WebAPK's meta data.
* @param metaData WebAPK meta data to extract the long from.
* @param name Name of the <meta-data> tag to extract the value from.
* @param defaultValue Value to return if long value could not be extracted.
* @return long value.
*/
private static long getLongFromMetaData(Bundle metaData, String name, long defaultValue) {
String value = metaData.getString(name);
// The value should be terminated with 'L' to force the value to be a string. According to
// https://developer.android.com/guide/topics/manifest/meta-data-element.html numeric
// meta data values can only be retrieved via {@link Bundle#getInt()} and
// {@link Bundle#getFloat()}. We cannot use {@link Bundle#getFloat()} due to loss of
// precision.
if (value == null || !value.endsWith("L")) {
return defaultValue;
}
try {
return Long.parseLong(value.substring(0, value.length() - 1));
} catch (NumberFormatException e) {
}
return defaultValue;
}
/**
* Extract the icon URLs and icon hashes from the WebAPK's meta data, and returns a map of these
* {URL, hash} pairs. The icon URLs/icon hashes are stored in a single meta data tag in the
......
......@@ -7,14 +7,10 @@ package org.chromium.chrome.browser.webapps;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.base.ContextUtils;
......@@ -32,6 +28,7 @@ import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.webapps.WebappActivity.ActivityType;
import org.chromium.net.NetError;
import org.chromium.net.NetworkChangeNotifier;
import org.chromium.webapk.lib.common.splash.SplashLayout;
/** Shows and hides splash screen. */
class WebappSplashScreenController extends EmptyTabObserver {
......@@ -235,64 +232,38 @@ class WebappSplashScreenController extends EmptyTabObserver {
Context context = ContextUtils.getApplicationContext();
Resources resources = context.getResources();
Bitmap displayIcon = (splashImage == null) ? webappInfo.icon() : splashImage;
int minimiumSizeThreshold =
resources.getDimensionPixelSize(R.dimen.webapp_splash_image_size_minimum);
int bigThreshold =
resources.getDimensionPixelSize(R.dimen.webapp_splash_image_size_threshold);
DisplayMetrics metrics =
ContextUtils.getApplicationContext().getResources().getDisplayMetrics();
int displayIconSmallestEdge = 0;
if (displayIcon != null) {
displayIconSmallestEdge = Math.min(
displayIcon.getScaledWidth(metrics), displayIcon.getScaledHeight(metrics));
Bitmap displayIcon = splashImage;
boolean displayIconGenerated = false;
if (displayIcon == null) {
displayIcon = webappInfo.icon();
displayIconGenerated = webappInfo.isIconGenerated();
}
@SplashLayout.IconClassification
int displayIconClassification =
SplashLayout.classifyIcon(resources, displayIcon, displayIconGenerated);
// Inflate the correct layout for the image.
int layoutId;
if (displayIconSmallestEdge < minimiumSizeThreshold
|| (displayIcon == webappInfo.icon() && webappInfo.isIconGenerated())) {
if (displayIconClassification == SplashLayout.IconClassification.INVALID) {
mWebappUma.recordSplashscreenIconType(WebappUma.SplashScreenIconType.NONE);
layoutId = R.layout.webapp_splash_screen_no_icon;
} else {
// The size of the splash screen image determines which layout to use.
boolean isUsingSmallSplashImage = displayIconSmallestEdge <= bigThreshold;
if (isUsingSmallSplashImage) {
layoutId = R.layout.webapp_splash_screen_small;
} else {
layoutId = R.layout.webapp_splash_screen_large;
}
// Record stats about the splash screen.
@WebappUma.SplashScreenIconType
int splashScreenIconType;
if (splashImage == null) {
splashScreenIconType = WebappUma.SplashScreenIconType.FALLBACK;
} else if (isUsingSmallSplashImage) {
} else if (displayIconClassification == SplashLayout.IconClassification.SMALL) {
splashScreenIconType = WebappUma.SplashScreenIconType.CUSTOM_SMALL;
} else {
splashScreenIconType = WebappUma.SplashScreenIconType.CUSTOM;
}
mWebappUma.recordSplashscreenIconType(splashScreenIconType);
mWebappUma.recordSplashscreenIconSize(
Math.round(displayIconSmallestEdge / resources.getDisplayMetrics().density));
Math.round(displayIcon.getScaledWidth(resources.getDisplayMetrics())
/ resources.getDisplayMetrics().density));
}
ViewGroup subLayout =
(ViewGroup) LayoutInflater.from(context).inflate(layoutId, mSplashScreen, true);
// Set up the elements of the splash screen.
TextView appNameView = (TextView) subLayout.findViewById(R.id.webapp_splash_screen_name);
ImageView splashIconView =
(ImageView) subLayout.findViewById(R.id.webapp_splash_screen_icon);
appNameView.setText(webappInfo.name());
if (splashIconView != null) splashIconView.setImageBitmap(displayIcon);
if (ColorUtils.shouldUseLightForegroundOnBackground(backgroundColor)) {
appNameView.setTextColor(
ApiCompatibilityUtils.getColor(resources, R.color.webapp_splash_title_light));
}
SplashLayout.createLayout(context, mSplashScreen, displayIcon, displayIconClassification,
webappInfo.name(),
ColorUtils.shouldUseLightForegroundOnBackground(backgroundColor));
if (mNativeLoaded) mWebappUma.commitMetrics();
}
......
......@@ -8,11 +8,25 @@ android_library("common_java") {
java_files = [
"src/org/chromium/webapk/lib/common/WebApkConstants.java",
"src/org/chromium/webapk/lib/common/WebApkMetaDataKeys.java",
"src/org/chromium/webapk/lib/common/WebApkMetaDataUtils.java",
"src/org/chromium/webapk/lib/common/WebApkVersionUtils.java",
]
srcjar_deps = [ "//chrome/android/webapk/libs/common:identity_service_aidl" ]
}
android_library("splash_java") {
java_files = [ "src/org/chromium/webapk/lib/common/splash/SplashLayout.java" ]
deps = [
":splash_resources",
"//third_party/android_deps:android_support_annotations_java",
]
}
android_resources("splash_resources") {
resource_dirs = [ "res_splash" ]
custom_package = "org.chromium.webapk.lib.common.splash"
}
android_aidl("identity_service_aidl") {
import_include = [ "src/org/chromium/webapk/lib/common/identity_service" ]
interface_file =
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!-- The spec offsets the center of the ImageView from the center of the screen by either 16 or
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!-- Unlike with the other two splash screen layouts, this one shows no image and the BOTTOM of
......
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 The Chromium Authors. All rights reserved.
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<!-- The spec offsets the center of the ImageView from the center of the screen by either 16 or
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources>
<dimen name="webapp_splash_offset">48dp</dimen>
<dimen name="webapp_splash_large_title_margin_bottom">8dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="WebappSplashScreenText" parent="android:TextAppearance">
<item name="android:textColor">@color/webapp_grey_900</item>
<item name="android:textSize">24sp</item>
<item name="android:fontFamily">sans-serif-condensed</item>
</style>
<style name="WebappSplashScreenTextTheme">
<item name="android:ellipsize">end</item>
<item name="android:gravity">center</item>
<item name="android:maxLines">2</item>
<item name="android:textAppearance">@style/WebappSplashScreenText</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<color name="webapp_grey_900">#202124</color>
<color name="webapp_splash_title_light">@android:color/white</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources>
<dimen name="webapp_splash_offset">32dp</dimen>
<dimen name="webapp_splash_image_size_ideal">128dp</dimen>
<dimen name="webapp_splash_image_size_threshold">80dp</dimen>
<dimen name="webapp_splash_image_size_minimum">48dp</dimen>
<dimen name="webapp_splash_small_image_size">80dp</dimen>
<dimen name="webapp_splash_small_title_margin_top">32dp</dimen>
<dimen name="webapp_splash_large_title_margin_bottom">16dp</dimen>
</resources>
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.lib.common;
import android.os.Bundle;
/** Contains utility methods for extracting WebAPK's meta data. */
public class WebApkMetaDataUtils {
/**
* Extracts long value from the WebAPK's meta data.
* @param metaData WebAPK meta data to extract the long from.
* @param name Name of the <meta-data> tag to extract the value from.
* @param defaultValue Value to return if long value could not be extracted.
* @return long value.
*/
public static long getLongFromMetaData(Bundle metaData, String name, long defaultValue) {
String value = metaData.getString(name);
// The value should be terminated with 'L' to force the value to be a string. According to
// https://developer.android.com/guide/topics/manifest/meta-data-element.html numeric
// meta data values can only be retrieved via {@link Bundle#getInt()} and
// {@link Bundle#getFloat()}. We cannot use {@link Bundle#getFloat()} due to loss of
// precision.
if (value == null || !value.endsWith("L")) {
return defaultValue;
}
try {
return Long.parseLong(value.substring(0, value.length() - 1));
} catch (NumberFormatException e) {
}
return defaultValue;
}
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.lib.common.splash;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.graphics.Bitmap;
import android.os.Build;
import android.support.annotation.IntDef;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Contains utility methods for drawing splash screen. The methods are applicable for both home
* screen shortcuts and WebAPKs.
*/
public class SplashLayout {
@IntDef({IconClassification.INVALID, IconClassification.SMALL, IconClassification.LARGE})
@Retention(RetentionPolicy.SOURCE)
public @interface IconClassification {
int INVALID = 0;
int SMALL = 1;
int LARGE = 2;
}
/**
* Classifies the icon based on:
* - Whether it is appropriate to display on the splash screen.
* - The icon size.
* Returns {@link IconClassification.INVALID} if the icon is inappropriate to display on the
* splash screen.
*/
public static @IconClassification int classifyIcon(
Resources resources, Bitmap icon, boolean wasIconGenerated) {
if (icon == null || wasIconGenerated) {
return IconClassification.INVALID;
}
DisplayMetrics metrics = resources.getDisplayMetrics();
int smallestEdge = Math.min(icon.getScaledWidth(metrics), icon.getScaledHeight(metrics));
int minimumSizeThreshold =
resources.getDimensionPixelSize(R.dimen.webapp_splash_image_size_minimum);
if (smallestEdge < minimumSizeThreshold) {
return IconClassification.INVALID;
}
int bigThreshold =
resources.getDimensionPixelSize(R.dimen.webapp_splash_image_size_threshold);
return (smallestEdge <= bigThreshold) ? IconClassification.SMALL : IconClassification.LARGE;
}
/** Returns the layout for the splash screen given the classification of the splash icon. */
private static int selectLayoutFromIconClassification(
@IconClassification int iconClassification) {
switch (iconClassification) {
case IconClassification.INVALID:
return R.layout.webapp_splash_screen_no_icon;
case IconClassification.SMALL:
return R.layout.webapp_splash_screen_small;
case IconClassification.LARGE:
default:
return R.layout.webapp_splash_screen_large;
}
}
/**
* @see android.content.res.Resources#getColor(int id).
*/
@SuppressWarnings("deprecation")
public static int getColorCompatibility(Resources res, int id) throws NotFoundException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return res.getColor(id, null);
} else {
return res.getColor(id);
}
}
/** Builds splash screen and attaches it to the parent view. */
public static void createLayout(Context appContext, ViewGroup parentView, Bitmap icon,
@IconClassification int iconClassification, String text, boolean useLightTextColor) {
int layoutId = selectLayoutFromIconClassification(iconClassification);
ViewGroup layout =
(ViewGroup) LayoutInflater.from(appContext).inflate(layoutId, parentView, true);
TextView appNameView = (TextView) layout.findViewById(R.id.webapp_splash_screen_name);
appNameView.setText(text);
if (useLightTextColor) {
appNameView.setTextColor(getColorCompatibility(
appContext.getResources(), R.color.webapp_splash_title_light));
}
ImageView splashIconView = (ImageView) layout.findViewById(R.id.webapp_splash_screen_icon);
if (splashIconView != null) splashIconView.setImageBitmap(icon);
}
}
......@@ -6,12 +6,10 @@ import("//build/config/android/rules.gni")
import("manifest_processor.gni")
import("shell_apk_version.gni")
android_resources("shell_apk_resources") {
resource_dirs = [ "res" ]
custom_package = "org.chromium.webapk.shell_apk"
deps = [
":webapk_strings_grd",
]
android_resources("new_splash_resources") {
custom_package = "org.chromium.webapk.shell_apk.h2o"
resource_dirs = [ "res_h2o" ]
deps = []
}
# Stamped out copy of the runtime-library, used for fail-safe code in when using an
......@@ -50,9 +48,14 @@ template("webapk_java") {
deps += [
":compiled_in_runtime_library_java",
":dex_loader_java",
":shell_apk_resources",
"//chrome/android/webapk/libs/common:common_java",
]
if (invoker.use_new_splash) {
java_files +=
[ "src/org/chromium/webapk/shell_apk/h2o/SplashActivity.java" ]
deps += [ "//chrome/android/webapk/libs/common:splash_java" ]
}
}
}
......@@ -61,20 +64,43 @@ template("webapk_tmpl") {
_manifest_output =
"${target_gen_dir}/${_manifest_target_name}/AndroidManifest.xml"
_java_target_name = "${target_name}_java"
_resources_target_name = "${target_name}_resources"
_use_new_splash = false
if (defined(invoker.use_new_splash)) {
_use_new_splash = invoker.use_new_splash
}
manifest_processor_template(_manifest_target_name) {
forward_variables_from(invoker, [ "config_file" ])
input = "AndroidManifest.xml"
if (_use_new_splash) {
input = "h2o/AndroidManifest.xml"
} else {
input = "AndroidManifest.xml"
}
output = _manifest_output
extra_variables = [ "shell_apk_version=$template_shell_apk_version" ]
}
android_resources(_resources_target_name) {
custom_package = "org.chromium.webapk.shell_apk"
resource_dirs = [ "res" ]
deps = [
":webapk_strings_grd",
]
if (_use_new_splash) {
deps += [ ":new_splash_resources" ]
}
}
webapk_java(_java_target_name) {
android_manifest_for_lint = _manifest_output
use_new_splash = _use_new_splash
deps = [
":$_manifest_target_name",
":$_resources_target_name",
]
}
......@@ -180,6 +206,12 @@ webapk_tmpl("http_webapk") {
apk_name = "HttpWebApk"
}
webapk_tmpl("new_splash_webapk") {
use_new_splash = true
config_file = "new_splash_manifest_config.json"
apk_name = "NewSplashWebApk"
}
android_library("shell_apk_javatests") {
testonly = true
java_files =
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="{{{manifest_package}}}"
android:versionCode="{{{version_code}}}"
android:versionName="{{{version_name}}}" >
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="26" />
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/short_name"
android:allowBackup="false"
android:supportsRtl="true">
<activity android:name="org.chromium.webapk.shell_apk.h2o.SplashActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="org.chromium.webapk.shell_apk.shellApkVersion" android:value="{{{shell_apk_version}}}" />
{{#bound_webapk}}
<meta-data android:name="org.chromium.webapk.shell_apk.runtimeHost" android:value="{{{runtime_host}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.runtimeHostApplicationName" android:value="{{{runtime_host_application_name}}}" />
{{/bound_webapk}}
<meta-data android:name="org.chromium.webapk.shell_apk.startUrl" android:value="{{{start_url}}}" />
{{#logged_intent_url_param}}
<meta-data android:name="org.chromium.webapk.shell_apk.loggedIntentUrlParam" android:value="{{{logged_intent_url_param}}}" />
{{/logged_intent_url_param}}
<meta-data android:name="org.chromium.webapk.shell_apk.scope" android:value="{{{scope_url}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.displayMode" android:value="{{{display_mode}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.orientation" android:value="{{{orientation}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.themeColor" android:value="{{{theme_color}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.backgroundColor" android:value="{{{background_color}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.hasLargeSplashIcons" android:value="{{{has_large_splash_icons}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.iconId" android:resource="@mipmap/app_icon" />
<meta-data android:name="org.chromium.webapk.shell_apk.splashId" android:resource="@drawable/splash_icon" />
<meta-data android:name="org.chromium.webapk.shell_apk.distributor" android:value="{{{distributor}}}" />
{{! Hashes of icons should be taken of the icons as they are available on the web. The icon
bytes should not be transformed (e.g. decoded / encoded) prior to taking the hash.
}}
<meta-data android:name="org.chromium.webapk.shell_apk.iconUrlsAndIconMurmur2Hashes" android:value="{{{icon_urls_and_icon_murmur2_hashes}}}" />
<meta-data android:name="org.chromium.webapk.shell_apk.webManifestUrl" android:value="{{{web_manifest_url}}}" />
{{#badge_icon_id}}<meta-data android:name="org.chromium.webapk.shell_apk.badgeIconId" android:resource="{{{badge_icon_id}}}" />{{/badge_icon_id}}
</application>
</manifest>
{
"manifest_package": "org.chromium.webapk.new.splash",
"scope_url": "https://pwa.rocks/",
"intent_filters": {
"scope_url_scheme": "https",
"scope_url_host": "pwa.rocks",
"scope_url_path_type": "android:pathPrefix",
"scope_url_path": "/"
},
"start_url": "https://pwa.rocks/",
"display_mode": "standalone",
"orientation": "portrait",
"theme_color": "2147483648L",
"background_color": "0L",
"has_large_splash_icons": "false",
"icon_urls_and_icon_murmur2_hashes": "http://www.pwa.rocks/icon1.png 0 http://www.pwa.rocks/icon2.png 0",
"web_manifest_url": "https://pwa.rocks/pwa.webmanifest",
"version_code": "1",
"version_name": "1.0",
"bound_webapk": {
"runtime_host": "com.google.android.apps.chrome",
"runtime_host_application_name": "Chromium"
},
"share_template": [{
"index": "0",
"title": "Share All",
"url_template": "https://pwa.rocks/share_public?title={title}&amp;text={text}&amp;url={url}"
},
{
"index": "1",
"title": "Share Title",
"url_template": "https://pwa.rocks/share_private?title={title}"
}]
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="SplashTheme" parent="@android:style/Theme.Holo">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@color/background_color</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<resources>
<color name="background_color">#F8F9FA</color>
</resources>
......@@ -6,7 +6,7 @@
# (including AndroidManifest.xml) is updated. This version should be incremented
# prior to uploading a new ShellAPK to the WebAPK Minting Server.
# Does not affect Chrome.apk
template_shell_apk_version = 52
template_shell_apk_version = 53
# The ShellAPK version expected by Chrome. Chrome will try to update the WebAPK
# if the WebAPK's ShellAPK version is less than |expected_shell_apk_version|.
......
......@@ -11,12 +11,18 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
......@@ -30,6 +36,9 @@ public class WebApkUtils {
private static final int MINIMUM_REQUIRED_CHROME_VERSION = 57;
private static final String TAG = "cr_WebApkUtils";
/** Percentage to darken a color by when setting the status bar color. */
private static final float DARKEN_COLOR_FRACTION = 0.6f;
private static final float CONTRAST_LIGHT_ITEM_THRESHOLD = 3f;
/** Returns whether the application is installed and enabled. */
......@@ -152,6 +161,84 @@ public class WebApkUtils {
return version < MINIMUM_REQUIRED_CHROME_VERSION;
}
/**
* Calculates the contrast between the given color and white, using the algorithm provided by
* the WCAG v1 in http://www.w3.org/TR/WCAG20/#contrast-ratiodef.
*/
private static float getContrastForColor(int color) {
float bgR = Color.red(color) / 255f;
float bgG = Color.green(color) / 255f;
float bgB = Color.blue(color) / 255f;
bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
return Math.abs((1.05f) / (bgL + 0.05f));
}
/**
* Darkens the given color to use on the status bar.
* @param color Color which should be darkened.
* @return Color that should be used for Android status bar.
*/
public static int getDarkenedColorForStatusBar(int color) {
return getDarkenedColor(color, DARKEN_COLOR_FRACTION);
}
/**
* Darken a color to a fraction of its current brightness.
* @param color The input color.
* @param darkenFraction The fraction of the current brightness the color should be.
* @return The new darkened color.
*/
public static int getDarkenedColor(int color, float darkenFraction) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= darkenFraction;
return Color.HSVToColor(hsv);
}
/**
* Check whether lighter or darker foreground elements (i.e. text, drawables etc.)
* should be used depending on the given background color.
* @param backgroundColor The background color value which is being queried.
* @return Whether light colored elements should be used.
*/
public static boolean shouldUseLightForegroundOnBackground(int backgroundColor) {
return getContrastForColor(backgroundColor) >= CONTRAST_LIGHT_ITEM_THRESHOLD;
}
/**
* Decodes bitmap drawable from WebAPK's resources. This should also be used for XML aliases.
*/
@SuppressWarnings("deprecation")
public static Bitmap decodeBitmapFromDrawable(Resources resources, int resourceId) {
if (resourceId == 0) {
return null;
}
try {
Drawable drawable = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
drawable = resources.getDrawable(resourceId, null);
} else {
drawable = resources.getDrawable(resourceId);
}
return drawable != null ? ((BitmapDrawable) drawable).getBitmap() : null;
} catch (Resources.NotFoundException e) {
return null;
}
}
/**
* @see android.view.Window#setStatusBarColor(int color).
*/
public static void setStatusBarColor(Window window, int statusBarColor) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(statusBarColor);
}
/**
* Returns the Intent to query a list of installed browser apps.
*/
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.webapk.shell_apk.h2o;
import android.app.Activity;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.widget.FrameLayout;
import org.chromium.webapk.lib.common.WebApkMetaDataKeys;
import org.chromium.webapk.lib.common.WebApkMetaDataUtils;
import org.chromium.webapk.lib.common.splash.SplashLayout;
import org.chromium.webapk.shell_apk.R;
import org.chromium.webapk.shell_apk.WebApkUtils;
/** Displays splash screen. */
public class SplashActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView();
}
private void setContentView() {
Bundle metadata = WebApkUtils.readMetaData(this);
Resources resources = getResources();
Bitmap icon = WebApkUtils.decodeBitmapFromDrawable(resources, R.drawable.splash_icon);
@SplashLayout.IconClassification
int iconClassification = SplashLayout.classifyIcon(resources, icon, false);
FrameLayout layout = new FrameLayout(this);
setContentView(layout);
int backgroundColor = WebApkUtils.getColor(resources, R.color.background_color);
SplashLayout.createLayout(this, layout, icon, iconClassification,
resources.getString(R.string.name),
WebApkUtils.shouldUseLightForegroundOnBackground(backgroundColor));
int themeColor = (int) WebApkMetaDataUtils.getLongFromMetaData(
metadata, WebApkMetaDataKeys.THEME_COLOR, Color.BLACK);
WebApkUtils.setStatusBarColor(
getWindow(), WebApkUtils.getDarkenedColorForStatusBar(themeColor));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment