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.chrome.browser.webapps; | 5 package org.chromium.chrome.browser.webapps; |
6 | 6 |
7 import android.app.Activity; | 7 import android.app.Activity; |
8 import android.content.Intent; | 8 import android.content.Intent; |
9 import android.net.Uri; | 9 import android.net.Uri; |
10 import android.os.Build; | 10 import android.os.Build; |
11 import android.os.Bundle; | 11 import android.os.Bundle; |
12 import android.util.Base64; | 12 import android.util.Base64; |
13 | 13 |
14 import org.chromium.base.ApiCompatibilityUtils; | 14 import org.chromium.base.ApiCompatibilityUtils; |
15 import org.chromium.base.ApplicationStatus; | 15 import org.chromium.base.ApplicationStatus; |
| 16 import org.chromium.base.CommandLine; |
16 import org.chromium.base.ContextUtils; | 17 import org.chromium.base.ContextUtils; |
17 import org.chromium.base.Log; | 18 import org.chromium.base.Log; |
| 19 import org.chromium.chrome.browser.ChromeSwitches; |
18 import org.chromium.chrome.browser.IntentHandler; | 20 import org.chromium.chrome.browser.IntentHandler; |
19 import org.chromium.chrome.browser.ShortcutHelper; | 21 import org.chromium.chrome.browser.ShortcutHelper; |
20 import org.chromium.chrome.browser.document.ChromeLauncherActivity; | 22 import org.chromium.chrome.browser.document.ChromeLauncherActivity; |
21 import org.chromium.chrome.browser.metrics.LaunchMetrics; | 23 import org.chromium.chrome.browser.metrics.LaunchMetrics; |
22 import org.chromium.chrome.browser.tab.Tab; | 24 import org.chromium.chrome.browser.tab.Tab; |
23 import org.chromium.chrome.browser.util.IntentUtils; | 25 import org.chromium.chrome.browser.util.IntentUtils; |
| 26 import org.chromium.webapk.lib.common.WebApkConstants; |
24 | 27 |
25 import java.lang.ref.WeakReference; | 28 import java.lang.ref.WeakReference; |
26 | 29 |
27 /** | 30 /** |
28 * Launches web apps. This was separated from the ChromeLauncherActivity becaus
e the | 31 * Launches web apps. This was separated from the ChromeLauncherActivity becaus
e the |
29 * ChromeLauncherActivity is not allowed to be excluded from Android's Recents:
crbug.com/517426. | 32 * ChromeLauncherActivity is not allowed to be excluded from Android's Recents:
crbug.com/517426. |
30 */ | 33 */ |
31 public class WebappLauncherActivity extends Activity { | 34 public class WebappLauncherActivity extends Activity { |
32 /** | 35 /** |
33 * Action fired when an Intent is trying to launch a WebappActivity. | 36 * Action fired when an Intent is trying to launch a WebappActivity. |
(...skipping 10 matching lines...) Expand all Loading... |
44 if (webappInfo == null) { | 47 if (webappInfo == null) { |
45 super.onCreate(null); | 48 super.onCreate(null); |
46 ApiCompatibilityUtils.finishAndRemoveTask(this); | 49 ApiCompatibilityUtils.finishAndRemoveTask(this); |
47 return; | 50 return; |
48 } | 51 } |
49 | 52 |
50 super.onCreate(savedInstanceState); | 53 super.onCreate(savedInstanceState); |
51 Intent intent = getIntent(); | 54 Intent intent = getIntent(); |
52 String webappId = webappInfo.id(); | 55 String webappId = webappInfo.id(); |
53 String webappUrl = webappInfo.uri().toString(); | 56 String webappUrl = webappInfo.uri().toString(); |
| 57 String webApkPackageName = webappInfo.webApkPackageName(); |
54 int webappSource = webappInfo.source(); | 58 int webappSource = webappInfo.source(); |
55 | 59 |
56 if (webappId != null && webappUrl != null) { | 60 if (webappId != null && webappUrl != null) { |
57 String webappMacString = IntentUtils.safeGetStringExtra( | 61 String webappMacString = IntentUtils.safeGetStringExtra( |
58 intent, ShortcutHelper.EXTRA_MAC); | 62 intent, ShortcutHelper.EXTRA_MAC); |
59 byte[] webappMac = | 63 byte[] webappMac = |
60 webappMacString == null ? null : Base64.decode(webappMacStri
ng, Base64.DEFAULT); | 64 webappMacString == null ? null : Base64.decode(webappMacStri
ng, Base64.DEFAULT); |
61 | 65 |
62 Intent launchIntent = null; | 66 Intent launchIntent = null; |
63 | 67 |
64 // Permit the launch to a standalone web app frame if the intent was
sent by Chrome, or | 68 // Permit the launch to a standalone web app frame if the intent was
sent by Chrome, or |
65 // if the MAC is present and valid for the URL to be opened. | 69 // if the MAC is present and valid for the URL to be opened. |
66 boolean isTrusted = IntentHandler.wasIntentSenderChrome(intent, | 70 boolean isTrusted = IntentHandler.wasIntentSenderChrome(intent, |
67 ContextUtils.getApplicationContext()); | 71 ContextUtils.getApplicationContext()); |
68 boolean isUrlValid = (webappMac != null | 72 boolean isUrlValid = (webappMac != null |
69 && WebappAuthenticator.isUrlValid(this, webappUrl, webappMac
)); | 73 && WebappAuthenticator.isUrlValid(this, webappUrl, webappMac
)); |
| 74 boolean isValidWebApk = isValidWebApk(webApkPackageName); |
| 75 if (webApkPackageName != null && !isValidWebApk) { |
| 76 isUrlValid = false; |
| 77 } |
70 | 78 |
71 if (isTrusted || isUrlValid) { | 79 if (isTrusted || isUrlValid) { |
72 LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(webap
pUrl, webappSource); | 80 LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(webap
pUrl, webappSource); |
73 | 81 launchIntent = createWebappLaunchIntent(webappInfo, isValidWebAp
k); |
74 String activityName = WebappActivity.class.getName(); | |
75 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { | |
76 // Specifically assign the app to a particular WebappActivit
y instance. | |
77 int activityIndex = ActivityAssigner.instance(this).assign(w
ebappId); | |
78 activityName += String.valueOf(activityIndex); | |
79 } | |
80 | |
81 // Create an intent to launch the Webapp in an unmapped WebappAc
tivity. | |
82 launchIntent = new Intent(); | |
83 launchIntent.setClassName(this, activityName); | |
84 webappInfo.setWebappIntentExtras(launchIntent); | |
85 | |
86 // On L+, firing intents with the exact same data should relaunc
h a particular | |
87 // Activity. | |
88 launchIntent.setAction(Intent.ACTION_VIEW); | |
89 launchIntent.setData(Uri.parse(WebappActivity.WEBAPP_SCHEME + ":
//" + webappId)); | |
90 } else { | 82 } else { |
91 Log.e(TAG, "Shortcut (%s) opened in Chrome.", webappUrl); | 83 Log.e(TAG, "Shortcut (%s) opened in Chrome.", webappUrl); |
92 | 84 |
93 // The shortcut data doesn't match the current encoding. Change
the intent action | 85 // The shortcut data doesn't match the current encoding. Change
the intent action |
94 // launch the URL with a VIEW Intent in the regular browser. | 86 // launch the URL with a VIEW Intent in the regular browser. |
95 launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webappUr
l)); | 87 launchIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(webappUr
l)); |
96 launchIntent.setClassName(getPackageName(), ChromeLauncherActivi
ty.class.getName()); | 88 launchIntent.setClassName(getPackageName(), ChromeLauncherActivi
ty.class.getName()); |
97 launchIntent.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE
_NEW_TAB, true); | 89 launchIntent.putExtra(ShortcutHelper.REUSE_URL_MATCHING_TAB_ELSE
_NEW_TAB, true); |
98 launchIntent.putExtra(ShortcutHelper.EXTRA_SOURCE, webappSource)
; | 90 launchIntent.putExtra(ShortcutHelper.EXTRA_SOURCE, webappSource)
; |
| 91 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
| 92 | ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
99 } | 93 } |
100 | 94 |
101 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | |
102 | ApiCompatibilityUtils.getActivityNewDocumentFlag()); | |
103 startActivity(launchIntent); | 95 startActivity(launchIntent); |
104 } | 96 } |
105 | 97 |
106 ApiCompatibilityUtils.finishAndRemoveTask(this); | 98 ApiCompatibilityUtils.finishAndRemoveTask(this); |
107 } | 99 } |
108 | 100 |
109 /** | 101 /** |
| 102 * Creates an Intent to launch the web app. |
| 103 * @param info Information about the web app. |
| 104 * @param isWebApk If true, launch the app as a WebApkActivity. If false, l
aunch the app as |
| 105 * a WebappActivity. |
| 106 */ |
| 107 private Intent createWebappLaunchIntent(WebappInfo info, boolean isWebApk) { |
| 108 String activityName = isWebApk ? WebApkActivity.class.getName() |
| 109 : WebappActivity.class.getName(); |
| 110 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |
| 111 // Specifically assign the app to a particular WebappActivity instan
ce. |
| 112 int activityIndex = ActivityAssigner.instance(info.id()).assign(info
.id()); |
| 113 activityName += String.valueOf(activityIndex); |
| 114 } |
| 115 |
| 116 // Create an intent to launch the Webapp in an unmapped WebappActivity. |
| 117 Intent launchIntent = new Intent(); |
| 118 launchIntent.setClassName(this, activityName); |
| 119 info.setWebappIntentExtras(launchIntent); |
| 120 |
| 121 // On L+, firing intents with the exact same data should relaunch a part
icular |
| 122 // Activity. |
| 123 launchIntent.setAction(Intent.ACTION_VIEW); |
| 124 launchIntent.setData(Uri.parse(WebappActivity.WEBAPP_SCHEME + "://" + in
fo.id())); |
| 125 |
| 126 if (!isWebApk) { |
| 127 // For WebAPK, we don't start a new task for WebApkActivity, it is j
ust on top |
| 128 // of the WebAPK's main activity and in the same task. |
| 129 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
| 130 | ApiCompatibilityUtils.getActivityNewDocumentFlag()); |
| 131 } |
| 132 return launchIntent; |
| 133 } |
| 134 |
| 135 /** |
110 * Brings a live WebappActivity back to the foreground if one exists for the
given tab ID. | 136 * Brings a live WebappActivity back to the foreground if one exists for the
given tab ID. |
111 * @param tabId ID of the Tab to bring back to the foreground. | 137 * @param tabId ID of the Tab to bring back to the foreground. |
112 * @return True if a live WebappActivity was found, false otherwise. | 138 * @return True if a live WebappActivity was found, false otherwise. |
113 */ | 139 */ |
114 public static boolean bringWebappToFront(int tabId) { | 140 public static boolean bringWebappToFront(int tabId) { |
115 if (tabId == Tab.INVALID_TAB_ID) return false; | 141 if (tabId == Tab.INVALID_TAB_ID) return false; |
116 | 142 |
117 for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningA
ctivities()) { | 143 for (WeakReference<Activity> activityRef : ApplicationStatus.getRunningA
ctivities()) { |
118 Activity activity = activityRef.get(); | 144 Activity activity = activityRef.get(); |
119 if (activity == null || !(activity instanceof WebappActivity)) conti
nue; | 145 if (activity == null || !(activity instanceof WebappActivity)) conti
nue; |
120 | 146 |
121 WebappActivity webappActivity = (WebappActivity) activity; | 147 WebappActivity webappActivity = (WebappActivity) activity; |
122 if (webappActivity.getActivityTab() != null | 148 if (webappActivity.getActivityTab() != null |
123 && webappActivity.getActivityTab().getId() == tabId) { | 149 && webappActivity.getActivityTab().getId() == tabId) { |
124 Tab tab = webappActivity.getActivityTab(); | 150 Tab tab = webappActivity.getActivityTab(); |
125 tab.getTabWebContentsDelegateAndroid().activateContents(); | 151 tab.getTabWebContentsDelegateAndroid().activateContents(); |
126 return true; | 152 return true; |
127 } | 153 } |
128 } | 154 } |
129 | 155 |
130 return false; | 156 return false; |
131 } | 157 } |
| 158 |
| 159 /** |
| 160 * Checks whether the package being targeted is a valid WebAPK. |
| 161 * @param webapkPackageName The package name of the requested WebAPK. |
| 162 * @return true iff all validation criteria are met. |
| 163 */ |
| 164 private boolean isValidWebApk(String webapkPackageName) { |
| 165 // TODO(hanxi): Adds more validation checks. For example, whether the We
bAPK is signed |
| 166 // by the WebAPK Minting Server. |
| 167 return CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_WEBAPK) |
| 168 && webapkPackageName != null |
| 169 && webapkPackageName.startsWith(WebApkConstants.WEBAPK_PACKAGE_P
REFIX); |
| 170 } |
132 } | 171 } |
OLD | NEW |