// Copyright 2016 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.chrome.browser.webapps; import android.content.Intent; import org.chromium.base.ContextUtils; import org.chromium.base.library_loader.LibraryProcessType; import org.chromium.chrome.browser.ChromeApplication; import org.chromium.chrome.browser.ShortcutHelper; import org.chromium.chrome.browser.externalnav.ExternalNavigationParams; import org.chromium.chrome.browser.tab.InterceptNavigationDelegateImpl; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabDelegateFactory; import org.chromium.chrome.browser.tab.TabRedirectHandler; import org.chromium.components.navigation_interception.NavigationParams; import org.chromium.content.browser.ChildProcessCreationParams; import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.ui.base.PageTransition; import org.chromium.webapk.lib.client.WebApkServiceConnectionManager; /** * An Activity is designed for WebAPKs (native Android apps) and displays a webapp in a nearly * UI-less Chrome. */ public class WebApkActivity extends WebappActivity { /** Manages whether to check update for the WebAPK, and starts update check if needed. */ private WebApkUpdateManager mUpdateManager; @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // We could bring a WebAPK hosted WebappActivity to foreground and navigate it to a // different URL. For example, WebAPK "foo" is launched and navigates to // "www.foo.com/foo". In Chrome, user clicks a link "www.foo.com/bar" in Google search // results. After clicking the link, WebAPK "foo" is brought to foreground, and // loads the page of "www.foo.com/bar" at the same time. // The extra {@link ShortcutHelper.EXTRA_URL} provides the URL that the WebAPK will // navigate to. String overrideUrl = intent.getStringExtra(ShortcutHelper.EXTRA_URL); if (overrideUrl != null && isInitialized() && !overrideUrl.equals(getActivityTab().getUrl())) { getActivityTab().loadUrl( new LoadUrlParams(overrideUrl, PageTransition.AUTO_TOPLEVEL)); } } @Override protected void onStorageIsNull(final int backgroundColor) { // Register the WebAPK. It is possible that a WebAPK's meta data was deleted when user // cleared Chrome's data. When it is launched again, we know that the WebAPK is still // installed, so re-register it. WebappRegistry.registerWebapp( mWebappInfo.id(), new WebappRegistry.FetchWebappDataStorageCallback() { @Override public void onWebappDataStorageRetrieved(WebappDataStorage storage) { updateStorage(storage); // Initialize the update related timestamps to the registration time. storage.updateTimeOfLastCheckForUpdatedWebManifest(); storage.updateTimeOfLastWebApkUpdateRequestCompletion(); // The downloading of the splash screen image happens before a WebAPK's // package name is available. If we want to use the image in the first // launch, we need to cache the image, register the WebAPK and store the // image in the SharedPreference when the WebAPK is installed // (before the first launch), and delete the cached image if it's not // installed. Therefore, lots of complexity will be introduced. To simplify // the logic, WebAPKs are registered during the first launch, and don't // retrieve splash screen image but use app icon for initialization. initializeSplashScreenWidgets(backgroundColor, null); } }); } @Override protected TabDelegateFactory createTabDelegateFactory() { return new WebappDelegateFactory(this) { @Override public InterceptNavigationDelegateImpl createInterceptNavigationDelegate(Tab tab) { return new InterceptNavigationDelegateImpl(tab) { @Override public ExternalNavigationParams.Builder buildExternalNavigationParams( NavigationParams navigationParams, TabRedirectHandler tabRedirectHandler, boolean shouldCloseTab) { ExternalNavigationParams.Builder builder = super.buildExternalNavigationParams( navigationParams, tabRedirectHandler, shouldCloseTab); builder.setWebApkPackageName(getWebApkPackageName()); return builder; } }; } @Override public boolean canShowAppBanners(Tab tab) { // Do not show app banners for WebAPKs regardless of the current page URL. // A WebAPK can display a page outside of its WebAPK scope if a page within the // WebAPK scope navigates via JavaScript while the WebAPK is in the background. return false; } }; } @Override public void onStop() { super.onStop(); WebApkServiceConnectionManager.getInstance().disconnect( ContextUtils.getApplicationContext(), getWebApkPackageName()); } /** * Returns the WebAPK's package name. */ private String getWebApkPackageName() { return getWebappInfo().webApkPackageName(); } @Override public void onResume() { super.onResume(); // WebAPK hosts Chrome's renderer processes by declaring the Chrome's renderer service in // its AndroidManifest.xml. We set {@link ChildProcessCreationParams} for WebAPK's renderer // process so the {@link ChildProcessLauncher} knows which application's renderer // service to connect. initializeChildProcessCreationParams(true); } @Override protected void initializeChildProcessCreationParams() { // TODO(hanxi): crbug.com/611842. Investigates whether this function works for multiple // windows or with --site-per-process enabled. initializeChildProcessCreationParams(true); } @Override public void onDeferredStartup() { super.onDeferredStartup(); mUpdateManager = new WebApkUpdateManager(); mUpdateManager.updateIfNeeded(getActivityTab(), mWebappInfo); } @Override public void onPause() { super.onPause(); initializeChildProcessCreationParams(false); } /** * Initializes {@link ChildProcessCreationParams} as a WebAPK's renderer process if * {@link isForWebApk}} is true; as Chrome's child process otherwise. * @param isForWebApk: Whether the {@link ChildProcessCreationParams} is initialized as a * WebAPK renderer process. */ private void initializeChildProcessCreationParams(boolean isForWebApk) { ChromeApplication chrome = (ChromeApplication) ContextUtils.getApplicationContext(); ChildProcessCreationParams params = chrome.getChildProcessCreationParams(); if (isForWebApk) { int extraBindFlag = params == null ? 0 : params.getExtraBindFlags(); params = new ChildProcessCreationParams(getWebappInfo().webApkPackageName(), extraBindFlag, LibraryProcessType.PROCESS_CHILD); } ChildProcessCreationParams.set(params); } @Override protected void onDestroyInternal() { if (mUpdateManager != null) { mUpdateManager.destroy(); } super.onDestroyInternal(); } }