Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.webapk.shell_apk; | 5 package org.chromium.webapk.shell_apk; |
| 6 | 6 |
| 7 import android.app.Activity; | |
| 8 import android.content.ActivityNotFoundException; | 7 import android.content.ActivityNotFoundException; |
| 9 import android.content.Intent; | 8 import android.content.Intent; |
| 10 import android.content.pm.ApplicationInfo; | 9 import android.content.pm.ApplicationInfo; |
| 11 import android.content.pm.PackageManager; | 10 import android.content.pm.PackageManager; |
| 12 import android.content.pm.PackageManager.NameNotFoundException; | 11 import android.content.pm.PackageManager.NameNotFoundException; |
| 13 import android.net.Uri; | 12 import android.net.Uri; |
| 14 import android.os.Bundle; | 13 import android.os.Bundle; |
| 14 import android.support.v4.app.DialogFragment; | |
| 15 import android.support.v4.app.FragmentActivity; | |
| 16 import android.text.TextUtils; | |
| 15 import android.util.Log; | 17 import android.util.Log; |
| 16 | 18 |
| 17 import org.chromium.webapk.lib.common.WebApkConstants; | 19 import org.chromium.webapk.lib.common.WebApkConstants; |
| 18 import org.chromium.webapk.lib.common.WebApkMetaDataKeys; | 20 import org.chromium.webapk.lib.common.WebApkMetaDataKeys; |
| 19 | 21 |
| 20 import java.net.URISyntaxException; | 22 import java.net.MalformedURLException; |
| 23 import java.net.URL; | |
| 24 import java.util.Set; | |
| 21 | 25 |
| 22 /** | 26 /** |
| 23 * WebAPK's main Activity. | 27 * WebAPK's main Activity. |
| 24 */ | 28 */ |
| 25 public class MainActivity extends Activity { | 29 public class MainActivity |
| 30 extends FragmentActivity implements ChooseHostBrowserDialogFragment.Dial ogListener { | |
| 26 private static final String TAG = "cr_MainActivity"; | 31 private static final String TAG = "cr_MainActivity"; |
| 27 | 32 |
| 28 /** | 33 /** |
| 29 * Name of class which launches browser in WebAPK mode. | 34 * Name of class which launches browser in WebAPK mode. |
| 30 */ | 35 */ |
| 31 private static final String HOST_BROWSER_LAUNCHER_CLASS_NAME = | 36 private static final String HOST_BROWSER_LAUNCHER_CLASS_NAME = |
| 32 "org.chromium.webapk.lib.runtime_library.HostBrowserLauncher"; | 37 "org.chromium.webapk.lib.runtime_library.HostBrowserLauncher"; |
| 33 | 38 |
| 34 // Action for launching {@link WebappLauncherActivity}. Must stay in sync wi th | 39 // Action for launching {@link WebappLauncherActivity}. Must stay in sync wi th |
| 35 // {@link WebappLauncherActivity#ACTION_START_WEBAPP}. | 40 // {@link WebappLauncherActivity#ACTION_START_WEBAPP}. |
| 36 public static final String ACTION_START_WEBAPK = | 41 public static final String ACTION_START_WEBAPK = |
| 37 "com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_W EBAPP"; | 42 "com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_W EBAPP"; |
| 38 | 43 |
| 39 // Must stay in sync with | 44 // Must stay in sync with |
| 40 // {@link org.chromium.chrome.browser.ShortcutHelper#REUSE_URL_MATCHING_TAB_ ELSE_NEW_TAB}. | 45 // {@link org.chromium.chrome.browser.ShortcutHelper#REUSE_URL_MATCHING_TAB_ ELSE_NEW_TAB}. |
| 41 private static final String REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB = | 46 private static final String REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB = |
| 42 "REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB"; | 47 "REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB"; |
| 43 | 48 |
| 44 /** | 49 /** |
| 45 * Key for passing app icon id. | 50 * Key for passing app icon id. |
| 46 */ | 51 */ |
| 47 private static final String KEY_APP_ICON_ID = "app_icon_id"; | 52 private static final String KEY_APP_ICON_ID = "app_icon_id"; |
| 48 | 53 |
| 54 private String mOverrideUrl; | |
| 55 private String mStartUrl; | |
| 56 | |
| 57 // A dialog that asks user to choose runtime host to launch this WebAPK. | |
| 58 private DialogFragment mDialog; | |
| 49 /** | 59 /** |
| 50 * Creates install Intent. | 60 * Creates install Intent. |
| 51 * @param packageName Package to install. | 61 * @param packageName Package to install. |
| 52 * @return The intent. | 62 * @return The intent. |
| 53 */ | 63 */ |
| 54 public static Intent createInstallIntent(String packageName) { | 64 public static Intent createInstallIntent(String packageName) { |
| 55 String marketUrl = "market://details?id=" + packageName; | 65 String marketUrl = "market://details?id=" + packageName; |
| 56 return new Intent(Intent.ACTION_VIEW, Uri.parse(marketUrl)); | 66 return new Intent(Intent.ACTION_VIEW, Uri.parse(marketUrl)); |
| 57 } | 67 } |
| 58 | 68 |
| 59 @Override | 69 @Override |
| 60 protected void onCreate(Bundle savedInstanceState) { | 70 protected void onCreate(Bundle savedInstanceState) { |
| 61 super.onCreate(savedInstanceState); | 71 super.onCreate(savedInstanceState); |
| 62 launch(); | 72 launch(); |
|
pkotwicz
2017/05/24 04:19:29
I don't think that having a separate launch() func
Xi Han
2017/05/24 16:52:37
Agree, deleted.
| |
| 63 finish(); | |
| 64 } | 73 } |
| 65 | 74 |
| 66 /** | 75 /** |
| 67 * Launches WebAPK. | 76 * Launches WebAPK. |
| 68 */ | 77 */ |
| 69 private void launch() { | 78 private void launch() { |
| 70 String overrideUrl = getOverrideUrl(); | 79 mOverrideUrl = getOverrideUrl(); |
| 71 String startUrl = (overrideUrl != null) ? overrideUrl : getStartUrl(); | 80 mStartUrl = (mOverrideUrl != null) ? mOverrideUrl : getStartUrl(); |
| 72 if (startUrl == null) { | 81 if (mStartUrl == null) { |
| 82 finish(); | |
| 73 return; | 83 return; |
| 74 } | 84 } |
| 75 | 85 |
| 76 if (launchHostBrowserInWebApkMode(startUrl, overrideUrl)) { | 86 launchHostBrowserInWebApkMode(); |
| 77 return; | |
| 78 } | |
| 79 if (launchBrowser(startUrl)) { | |
| 80 return; | |
| 81 } | |
| 82 installBrowser(); | |
| 83 } | 87 } |
| 84 | 88 |
| 85 /** | 89 /** |
| 86 * Launches host browser in WebAPK mode. | 90 * Launches host browser in WebAPK mode. |
| 87 * @return True if successful. | 91 * @return True if successful. |
| 88 */ | 92 */ |
| 89 private boolean launchHostBrowserInWebApkMode(String startUrl, String overri deUrl) { | 93 private void launchHostBrowserInWebApkMode() { |
|
pkotwicz
2017/05/24 04:19:29
I think that it now makes sense to merge this func
Xi Han
2017/05/24 16:52:37
Acknowledged.
| |
| 90 Log.v(TAG, "Url of the WebAPK: " + startUrl); | 94 Log.v(TAG, "Url of the WebAPK: " + mStartUrl); |
| 91 String packageName = getPackageName(); | 95 String packageName = getPackageName(); |
| 92 Log.v(TAG, "Package name of the WebAPK:" + packageName); | 96 Log.v(TAG, "Package name of the WebAPK:" + packageName); |
| 93 | 97 |
| 94 String runtimeHost = WebApkUtils.getHostBrowserPackageName(this); | 98 String runtimeHost = WebApkUtils.getHostBrowserPackageName(this); |
| 95 boolean isFromExternalIntent = (overrideUrl != null); | 99 if (!TextUtils.isEmpty(runtimeHost) && launchInHostBrowser(runtimeHost)) { |
| 100 finish(); | |
| 101 } else { | |
|
pkotwicz
2017/05/24 04:19:29
My preference is to make the try/catch statement a
Xi Han
2017/05/24 16:52:37
Done.
| |
| 102 try { | |
| 103 mDialog = ChooseHostBrowserDialogFragment.newInstance(new URL(mS tartUrl).getHost()); | |
| 104 mDialog.show(getSupportFragmentManager(), "ChooseHostBrowserDial ogFragment"); | |
| 105 } catch (MalformedURLException e) { | |
| 106 Log.e(TAG, "Unable to create a dialog to choose the host browser ."); | |
| 107 finish(); | |
| 108 } | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 /** Retrieves URL from the intent's data. Returns null if a URL could not be retrieved. */ | |
| 113 private String getOverrideUrl() { | |
| 114 String overrideUrl = getIntent().getDataString(); | |
| 115 if (overrideUrl != null && overrideUrl.startsWith("https:")) { | |
| 116 return overrideUrl; | |
| 117 } | |
| 118 return null; | |
| 119 } | |
| 120 | |
| 121 /** Returns the start URL from the Android Manifest. */ | |
| 122 private String getStartUrl() { | |
| 123 ApplicationInfo appInfo; | |
| 124 try { | |
| 125 appInfo = getPackageManager().getApplicationInfo( | |
| 126 getPackageName(), PackageManager.GET_META_DATA); | |
| 127 } catch (NameNotFoundException e) { | |
| 128 return null; | |
| 129 } | |
| 130 return appInfo.metaData.getString(WebApkMetaDataKeys.START_URL); | |
| 131 } | |
| 132 | |
| 133 @Override | |
| 134 public void onHostBrowserSelected(String runtimeHost) { | |
| 135 Set<String> installedBrowsers = WebApkUtils.getInstalledBrowsers(getPack ageManager()); | |
| 136 if (installedBrowsers.contains(runtimeHost)) { | |
| 137 launchInHostBrowser(runtimeHost); | |
| 138 } else { | |
| 139 installBrowser(runtimeHost); | |
| 140 } | |
| 141 // It is safe to cache the runtimeHost to the share pref if user didn't install the browser | |
| 142 // in {@link installBrowser}. On next launch, {@link WebApkUtils#getHost BrowserPackageName} | |
| 143 // will catch it, and the dialog to choose host browser will show again. If user did install | |
| 144 // the browser chosen, saving the runtimeHost to the shared pref can avo id the dialog to | |
| 145 // show. | |
| 146 WebApkUtils.writeHostBrowserToSharedPref(this, runtimeHost); | |
| 147 finish(); | |
| 148 } | |
| 149 | |
| 150 @Override | |
| 151 public void onQuit() { | |
| 152 finish(); | |
| 153 } | |
| 154 | |
| 155 private boolean launchInHostBrowser(String runtimeHost) { | |
| 156 boolean isFromExternalIntent = (mOverrideUrl != null); | |
| 96 int source = getIntent().getIntExtra(WebApkConstants.EXTRA_SOURCE, 0); | 157 int source = getIntent().getIntExtra(WebApkConstants.EXTRA_SOURCE, 0); |
| 97 if (isFromExternalIntent && source == WebApkConstants.SHORTCUT_SOURCE_UN KNOWN) { | 158 if (isFromExternalIntent && source == WebApkConstants.SHORTCUT_SOURCE_UN KNOWN) { |
| 98 source = WebApkConstants.SHORTCUT_SOURCE_EXTERNAL_INTENT; | 159 source = WebApkConstants.SHORTCUT_SOURCE_EXTERNAL_INTENT; |
| 99 } | 160 } |
| 100 | 161 |
| 101 // The override URL is non null when the WebAPK is launched from a deep link. The WebAPK | 162 // The override URL is non null when the WebAPK is launched from a deep link. The WebAPK |
| 102 // should navigate to the URL in the deep link even if the WebAPK is alr eady open. | 163 // should navigate to the URL in the deep link even if the WebAPK is alr eady open. |
| 103 Intent intent = new Intent(); | 164 Intent intent = new Intent(); |
| 104 intent.setAction(ACTION_START_WEBAPK); | 165 intent.setAction(ACTION_START_WEBAPK); |
| 105 intent.setPackage(runtimeHost); | 166 intent.setPackage(runtimeHost); |
| 106 intent.putExtra(WebApkConstants.EXTRA_URL, startUrl) | 167 intent.putExtra(WebApkConstants.EXTRA_URL, mStartUrl) |
| 107 .putExtra(WebApkConstants.EXTRA_SOURCE, source) | 168 .putExtra(WebApkConstants.EXTRA_SOURCE, source) |
| 108 .putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, packageName ) | 169 .putExtra(WebApkConstants.EXTRA_WEBAPK_PACKAGE_NAME, getPackageN ame()) |
| 109 .putExtra(WebApkConstants.EXTRA_WEBAPK_FORCE_NAVIGATION, isFromE xternalIntent); | 170 .putExtra(WebApkConstants.EXTRA_WEBAPK_FORCE_NAVIGATION, isFromE xternalIntent); |
| 110 | 171 |
| 111 try { | 172 try { |
| 112 startActivity(intent); | 173 startActivity(intent); |
| 113 return true; | 174 return true; |
| 114 } catch (ActivityNotFoundException e) { | 175 } catch (ActivityNotFoundException e) { |
| 115 Log.w(TAG, "Unable to launch browser in WebAPK mode."); | 176 Log.w(TAG, "Unable to launch browser in WebAPK mode."); |
| 116 e.printStackTrace(); | 177 e.printStackTrace(); |
| 117 return false; | 178 return false; |
| 118 } | 179 } |
| 119 } | 180 } |
| 120 | 181 |
| 121 /** | 182 /** |
| 122 * Launches browser (not necessarily the host browser). | |
| 123 * @param startUrl URL to navigate browser to. | |
| 124 * @return True if successful. | |
| 125 */ | |
| 126 private boolean launchBrowser(String startUrl) { | |
| 127 Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(startUrl)); | |
| 128 intent.addCategory(Intent.CATEGORY_BROWSABLE); | |
| 129 | |
| 130 // The WebAPK can handle {@link startUrl}. Set a selector to prevent the WebAPK from | |
| 131 // launching itself. | |
| 132 try { | |
| 133 Intent selectorIntent = Intent.parseUri("https://", Intent.URI_INTEN T_SCHEME); | |
| 134 intent.setSelector(selectorIntent); | |
| 135 } catch (URISyntaxException e) { | |
| 136 return false; | |
| 137 } | |
| 138 | |
| 139 // Add extras in case that the URL is launched in Chrome. | |
| 140 int source = | |
| 141 getIntent().getIntExtra(WebApkConstants.EXTRA_SOURCE, Intent.URI _INTENT_SCHEME); | |
| 142 intent.putExtra(REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true) | |
| 143 .putExtra(WebApkConstants.EXTRA_SOURCE, source); | |
| 144 | |
| 145 try { | |
| 146 startActivity(intent); | |
| 147 } catch (ActivityNotFoundException e) { | |
| 148 return false; | |
| 149 } | |
| 150 return true; | |
| 151 } | |
| 152 | |
| 153 /** | |
| 154 * Launches the Play Store with the host browser's page. | 183 * Launches the Play Store with the host browser's page. |
| 155 */ | 184 */ |
| 156 private void installBrowser() { | 185 private void installBrowser(String hostBrowserPackageName) { |
| 157 String hostBrowserPackageName = WebApkUtils.getHostBrowserPackageName(th is); | |
| 158 if (hostBrowserPackageName == null) { | |
| 159 return; | |
| 160 } | |
| 161 | |
| 162 try { | 186 try { |
| 163 startActivity(createInstallIntent(hostBrowserPackageName)); | 187 startActivity(createInstallIntent(hostBrowserPackageName)); |
| 164 } catch (ActivityNotFoundException e) { | 188 } catch (ActivityNotFoundException e) { |
| 165 } | 189 } |
| 166 } | 190 } |
| 167 | |
| 168 /** Retrieves URL from the intent's data. Returns null if a URL could not be retrieved. */ | |
| 169 private String getOverrideUrl() { | |
| 170 String overrideUrl = getIntent().getDataString(); | |
| 171 if (overrideUrl != null && overrideUrl.startsWith("https:")) { | |
| 172 return overrideUrl; | |
| 173 } | |
| 174 return null; | |
| 175 } | |
| 176 | |
| 177 /** Returns the start URL from the Android Manifest. */ | |
| 178 private String getStartUrl() { | |
| 179 ApplicationInfo appInfo; | |
| 180 try { | |
| 181 appInfo = getPackageManager().getApplicationInfo( | |
| 182 getPackageName(), PackageManager.GET_META_DATA); | |
| 183 } catch (NameNotFoundException e) { | |
| 184 return null; | |
| 185 } | |
| 186 return appInfo.metaData.getString(WebApkMetaDataKeys.START_URL); | |
| 187 } | |
| 188 } | 191 } |
| OLD | NEW |