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.content.Intent; | 7 import android.content.Intent; |
8 import android.graphics.Bitmap; | 8 import android.graphics.Bitmap; |
9 import android.graphics.Color; | 9 import android.graphics.Color; |
10 import android.graphics.drawable.Drawable; | 10 import android.graphics.drawable.Drawable; |
11 import android.net.Uri; | 11 import android.net.Uri; |
12 import android.os.Build; | |
12 import android.os.Bundle; | 13 import android.os.Bundle; |
13 import android.os.StrictMode; | 14 import android.os.StrictMode; |
14 import android.os.SystemClock; | 15 import android.os.SystemClock; |
15 import android.text.TextUtils; | 16 import android.text.TextUtils; |
16 import android.view.LayoutInflater; | 17 import android.view.LayoutInflater; |
17 import android.view.View; | 18 import android.view.View; |
19 import android.view.View.OnSystemUiVisibilityChangeListener; | |
18 import android.view.ViewGroup; | 20 import android.view.ViewGroup; |
19 import android.widget.FrameLayout; | 21 import android.widget.FrameLayout; |
20 import android.widget.ImageView; | 22 import android.widget.ImageView; |
21 import android.widget.TextView; | 23 import android.widget.TextView; |
22 | 24 |
23 import org.chromium.base.ActivityState; | 25 import org.chromium.base.ActivityState; |
24 import org.chromium.base.ApiCompatibilityUtils; | 26 import org.chromium.base.ApiCompatibilityUtils; |
25 import org.chromium.base.ApplicationStatus; | 27 import org.chromium.base.ApplicationStatus; |
26 import org.chromium.base.Log; | 28 import org.chromium.base.Log; |
27 import org.chromium.base.VisibleForTesting; | 29 import org.chromium.base.VisibleForTesting; |
28 import org.chromium.base.metrics.RecordHistogram; | 30 import org.chromium.base.metrics.RecordHistogram; |
29 import org.chromium.blink_public.platform.WebDisplayMode; | 31 import org.chromium.blink_public.platform.WebDisplayMode; |
30 import org.chromium.chrome.R; | 32 import org.chromium.chrome.R; |
31 import org.chromium.chrome.browser.TabState; | 33 import org.chromium.chrome.browser.TabState; |
32 import org.chromium.chrome.browser.document.DocumentUtils; | 34 import org.chromium.chrome.browser.document.DocumentUtils; |
35 import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; | |
33 import org.chromium.chrome.browser.metrics.WebappUma; | 36 import org.chromium.chrome.browser.metrics.WebappUma; |
34 import org.chromium.chrome.browser.tab.EmptyTabObserver; | 37 import org.chromium.chrome.browser.tab.EmptyTabObserver; |
35 import org.chromium.chrome.browser.tab.Tab; | 38 import org.chromium.chrome.browser.tab.Tab; |
36 import org.chromium.chrome.browser.tab.TabDelegateFactory; | 39 import org.chromium.chrome.browser.tab.TabDelegateFactory; |
37 import org.chromium.chrome.browser.tab.TabObserver; | 40 import org.chromium.chrome.browser.tab.TabObserver; |
38 import org.chromium.chrome.browser.util.ColorUtils; | 41 import org.chromium.chrome.browser.util.ColorUtils; |
39 import org.chromium.chrome.browser.util.UrlUtilities; | 42 import org.chromium.chrome.browser.util.UrlUtilities; |
40 import org.chromium.content.browser.ScreenOrientationProvider; | 43 import org.chromium.content.browser.ScreenOrientationProvider; |
41 import org.chromium.content_public.browser.LoadUrlParams; | 44 import org.chromium.content_public.browser.LoadUrlParams; |
42 import org.chromium.net.NetworkChangeNotifier; | 45 import org.chromium.net.NetworkChangeNotifier; |
43 import org.chromium.ui.base.PageTransition; | 46 import org.chromium.ui.base.PageTransition; |
44 | 47 |
45 import java.io.File; | 48 import java.io.File; |
46 import java.util.concurrent.TimeUnit; | 49 import java.util.concurrent.TimeUnit; |
47 | 50 |
48 /** | 51 /** |
49 * Displays a webapp in a nearly UI-less Chrome (InfoBars still appear). | 52 * Displays a webapp in a nearly UI-less Chrome (InfoBars still appear). |
50 */ | 53 */ |
51 public class WebappActivity extends FullScreenActivity { | 54 public class WebappActivity extends FullScreenActivity { |
52 public static final String WEBAPP_SCHEME = "webapp"; | 55 public static final String WEBAPP_SCHEME = "webapp"; |
53 | 56 |
54 private static final String TAG = "WebappActivity"; | 57 private static final String TAG = "WebappActivity"; |
55 private static final long MS_BEFORE_NAVIGATING_BACK_FROM_INTERSTITIAL = 1000 ; | 58 private static final long MS_BEFORE_NAVIGATING_BACK_FROM_INTERSTITIAL = 1000 ; |
56 | 59 |
60 private static final int ENTER_IMMERSIVE_MODE_DELAY_MILLIS = 300; | |
61 private static final int RESTORE_IMMERSIVE_MODE_DELAY_MILLIS = 3000; | |
62 private static final int IMMERSIVE_MODE_UI_FLAGS = View.SYSTEM_UI_FLAG_LAYOU T_STABLE | |
63 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | |
64 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | |
65 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar | |
66 | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar | |
67 | View.SYSTEM_UI_FLAG_LOW_PROFILE | |
68 | View.SYSTEM_UI_FLAG_IMMERSIVE; | |
69 | |
57 private final WebappDirectoryManager mDirectoryManager; | 70 private final WebappDirectoryManager mDirectoryManager; |
58 | 71 |
59 protected WebappInfo mWebappInfo; | 72 protected WebappInfo mWebappInfo; |
60 | 73 |
61 private ViewGroup mSplashScreen; | 74 private ViewGroup mSplashScreen; |
62 private WebappUrlBar mUrlBar; | 75 private WebappUrlBar mUrlBar; |
63 | 76 |
64 private boolean mIsInitialized; | 77 private boolean mIsInitialized; |
65 private Integer mBrandColor; | 78 private Integer mBrandColor; |
66 | 79 |
67 private WebappUma mWebappUma; | 80 private WebappUma mWebappUma; |
68 | 81 |
69 private Bitmap mLargestFavicon; | 82 private Bitmap mLargestFavicon; |
70 | 83 |
84 private Runnable mSetImmersiveRunnable; | |
85 | |
71 /** | 86 /** |
72 * Construct all the variables that shouldn't change. We do it here both to clarify when the | 87 * Construct all the variables that shouldn't change. We do it here both to clarify when the |
73 * objects are created and to ensure that they exist throughout the parallel ized initialization | 88 * objects are created and to ensure that they exist throughout the parallel ized initialization |
74 * of the WebappActivity. | 89 * of the WebappActivity. |
75 */ | 90 */ |
76 public WebappActivity() { | 91 public WebappActivity() { |
77 mWebappInfo = createWebappInfo(null); | 92 mWebappInfo = createWebappInfo(null); |
78 mDirectoryManager = new WebappDirectoryManager(); | 93 mDirectoryManager = new WebappDirectoryManager(); |
79 mWebappUma = new WebappUma(); | 94 mWebappUma = new WebappUma(); |
80 } | 95 } |
81 | 96 |
82 @Override | 97 @Override |
83 protected void onNewIntent(Intent intent) { | 98 protected void onNewIntent(Intent intent) { |
84 if (intent == null) return; | 99 if (intent == null) return; |
85 super.onNewIntent(intent); | 100 super.onNewIntent(intent); |
86 | 101 |
87 WebappInfo newWebappInfo = createWebappInfo(intent); | 102 WebappInfo newWebappInfo = createWebappInfo(intent); |
88 if (newWebappInfo == null) { | 103 if (newWebappInfo == null) { |
89 Log.e(TAG, "Failed to parse new Intent: " + intent); | 104 Log.e(TAG, "Failed to parse new Intent: " + intent); |
90 finish(); | 105 finish(); |
91 } else if (!TextUtils.equals(mWebappInfo.id(), newWebappInfo.id())) { | 106 } else if (!TextUtils.equals(mWebappInfo.id(), newWebappInfo.id())) { |
92 mWebappInfo = newWebappInfo; | 107 mWebappInfo = newWebappInfo; |
93 resetSavedInstanceState(); | 108 resetSavedInstanceState(); |
94 if (mIsInitialized) initializeUI(null); | 109 if (mIsInitialized) initializeUI(null); |
95 // TODO(dominickn): send the web app into fullscreen if mDisplayMode is | |
96 // WebDisplayMode.Fullscreen. See crbug.com/581522 | |
97 } | 110 } |
98 } | 111 } |
99 | 112 |
100 protected boolean isInitialized() { | 113 protected boolean isInitialized() { |
101 return mIsInitialized; | 114 return mIsInitialized; |
102 } | 115 } |
103 | 116 |
104 protected WebappInfo createWebappInfo(Intent intent) { | 117 protected WebappInfo createWebappInfo(Intent intent) { |
105 return (intent == null) ? WebappInfo.createEmpty() : WebappInfo.create(i ntent); | 118 return (intent == null) ? WebappInfo.createEmpty() : WebappInfo.create(i ntent); |
106 } | 119 } |
107 | 120 |
108 private void initializeUI(Bundle savedInstanceState) { | 121 private void initializeUI(Bundle savedInstanceState) { |
109 // We do not load URL when restoring from saved instance states. | 122 // We do not load URL when restoring from saved instance states. |
110 if (savedInstanceState == null && mWebappInfo.isInitialized()) { | 123 if (savedInstanceState == null && mWebappInfo.isInitialized()) { |
111 if (TextUtils.isEmpty(getActivityTab().getUrl())) { | 124 if (TextUtils.isEmpty(getActivityTab().getUrl())) { |
112 getActivityTab().loadUrl(new LoadUrlParams( | 125 getActivityTab().loadUrl(new LoadUrlParams( |
113 mWebappInfo.uri().toString(), PageTransition.AUTO_TOPLEV EL)); | 126 mWebappInfo.uri().toString(), PageTransition.AUTO_TOPLEV EL)); |
114 } | 127 } |
115 } else { | 128 } else { |
116 if (NetworkChangeNotifier.isOnline()) getActivityTab().reloadIgnorin gCache(); | 129 if (NetworkChangeNotifier.isOnline()) getActivityTab().reloadIgnorin gCache(); |
117 } | 130 } |
118 | 131 |
119 getActivityTab().addObserver(createTabObserver()); | 132 getActivityTab().addObserver(createTabObserver()); |
120 getActivityTab().getTabWebContentsDelegateAndroid().setDisplayMode( | 133 getActivityTab().getTabWebContentsDelegateAndroid().setDisplayMode( |
121 WebDisplayMode.Standalone); | 134 mWebappInfo.displayMode()); |
122 // TODO(dominickn): send the web app into fullscreen if mDisplayMode is | 135 if (mWebappInfo.displayMode() == WebDisplayMode.Fullscreen) { |
123 // WebDisplayMode.Fullscreen. See crbug.com/581522 | 136 enterImmersiveMode(); |
137 } | |
124 } | 138 } |
125 | 139 |
126 @Override | 140 @Override |
127 public void preInflationStartup() { | 141 public void preInflationStartup() { |
128 WebappInfo info = createWebappInfo(getIntent()); | 142 WebappInfo info = createWebappInfo(getIntent()); |
129 | 143 |
130 String id = ""; | 144 String id = ""; |
131 if (info != null) { | 145 if (info != null) { |
132 mWebappInfo = info; | 146 mWebappInfo = info; |
133 id = info.id(); | 147 id = info.id(); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
196 long time = SystemClock.elapsedRealtime(); | 210 long time = SystemClock.elapsedRealtime(); |
197 TabState.saveState(tabFile, getActivityTab().getState(), false); | 211 TabState.saveState(tabFile, getActivityTab().getState(), false); |
198 RecordHistogram.recordTimesHistogram("Android.StrictMode.WebappSaveS tate", | 212 RecordHistogram.recordTimesHistogram("Android.StrictMode.WebappSaveS tate", |
199 SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS) ; | 213 SystemClock.elapsedRealtime() - time, TimeUnit.MILLISECONDS) ; |
200 } finally { | 214 } finally { |
201 StrictMode.setThreadPolicy(oldPolicy); | 215 StrictMode.setThreadPolicy(oldPolicy); |
202 } | 216 } |
203 } | 217 } |
204 | 218 |
205 @Override | 219 @Override |
220 public void onWindowFocusChanged(boolean hasFocus) { | |
221 super.onWindowFocusChanged(hasFocus); | |
222 | |
223 // Re-enter immersive mode after users switch back to this Activity. | |
224 if (hasFocus) { | |
225 asyncSetImmersive(ENTER_IMMERSIVE_MODE_DELAY_MILLIS); | |
226 } | |
227 } | |
228 | |
229 /** | |
230 * Sets activity's decor view into an immersive mode. | |
231 * If immersive mode is not supported, this method no-ops. | |
232 */ | |
233 private void enterImmersiveMode() { | |
234 // Immersive mode is only supported in API 19+. | |
235 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; | |
236 | |
237 if (mSetImmersiveRunnable == null) { | |
238 | |
239 final View decor = getWindow().getDecorView(); | |
240 | |
241 mSetImmersiveRunnable = new Runnable() { | |
242 @Override | |
243 public void run() { | |
244 int currentFlags = decor.getSystemUiVisibility(); | |
245 int desiredFlags = currentFlags | IMMERSIVE_MODE_UI_FLAGS; | |
246 if (currentFlags != desiredFlags) { | |
247 decor.setSystemUiVisibility(desiredFlags); | |
248 } | |
249 } | |
250 }; | |
251 | |
252 // When we enter immersive mode for the first time, register a | |
253 // SystemUiVisibilityChangeListener that restores immersive mode. Th is is necessary | |
254 // because user actions like focusing a keyboard will break out of i mmersive mode. | |
255 decor.setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibility ChangeListener() { | |
256 @Override | |
257 public void onSystemUiVisibilityChange(int newFlags) { | |
258 if ((newFlags & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { | |
259 asyncSetImmersive(RESTORE_IMMERSIVE_MODE_DELAY_MILLIS); | |
260 } | |
261 } | |
262 }); | |
263 } | |
264 | |
265 asyncSetImmersive(ENTER_IMMERSIVE_MODE_DELAY_MILLIS); | |
266 } | |
267 | |
268 /** | |
269 * This method no-ops before {@link enterImmersiveMode()) is called explicit ly. | |
270 */ | |
271 private void asyncSetImmersive(int delayInMills) { | |
272 if (mSetImmersiveRunnable == null) return; | |
273 | |
274 mHandler.removeCallbacks(mSetImmersiveRunnable); | |
275 mHandler.postDelayed(mSetImmersiveRunnable, delayInMills); | |
276 } | |
277 | |
278 @Override | |
206 public void onResume() { | 279 public void onResume() { |
207 if (!isFinishing()) { | 280 if (!isFinishing()) { |
208 if (getIntent() != null) { | 281 if (getIntent() != null) { |
209 // Avoid situations where Android starts two Activities with the same data. | 282 // Avoid situations where Android starts two Activities with the same data. |
210 DocumentUtils.finishOtherTasksWithData(getIntent().getData(), ge tTaskId()); | 283 DocumentUtils.finishOtherTasksWithData(getIntent().getData(), ge tTaskId()); |
211 } | 284 } |
212 updateTaskDescription(); | 285 updateTaskDescription(); |
213 } | 286 } |
214 super.onResume(); | 287 super.onResume(); |
215 } | 288 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
365 Tab tab = getActivityTab(); | 438 Tab tab = getActivityTab(); |
366 if (tab == null || mUrlBar == null) return; | 439 if (tab == null || mUrlBar == null) return; |
367 mUrlBar.update(tab.getUrl(), tab.getSecurityLevel()); | 440 mUrlBar.update(tab.getUrl(), tab.getSecurityLevel()); |
368 } | 441 } |
369 | 442 |
370 private boolean isWebappDomain() { | 443 private boolean isWebappDomain() { |
371 return UrlUtilities.sameDomainOrHost( | 444 return UrlUtilities.sameDomainOrHost( |
372 getActivityTab().getUrl(), getWebappInfo().uri().toString(), tru e); | 445 getActivityTab().getUrl(), getWebappInfo().uri().toString(), tru e); |
373 } | 446 } |
374 | 447 |
448 @Override | |
449 protected ChromeFullscreenManager createFullscreenManager() { | |
450 // Disable HTML5 fullscreen in PWA fullscreen mode. | |
451 return new ChromeFullscreenManager(this, false) { | |
Ted C
2017/02/17 05:51:50
I'm also not against returning null here assuming
Leo
2017/02/17 07:03:08
I prefer your suggestion. Even returning null is c
| |
452 @Override | |
453 public void setPersistentFullscreenMode(boolean enabled) { | |
454 if (mWebappInfo.displayMode() == WebDisplayMode.Fullscreen) retu rn; | |
455 super.setPersistentFullscreenMode(enabled); | |
456 } | |
457 | |
458 @Override | |
459 public boolean getPersistentFullscreenMode() { | |
460 if (mWebappInfo.displayMode() == WebDisplayMode.Fullscreen) retu rn false; | |
461 return super.getPersistentFullscreenMode(); | |
462 } | |
463 }; | |
464 } | |
465 | |
375 protected TabObserver createTabObserver() { | 466 protected TabObserver createTabObserver() { |
376 return new EmptyTabObserver() { | 467 return new EmptyTabObserver() { |
377 | 468 |
378 @Override | 469 @Override |
379 public void onSSLStateUpdated(Tab tab) { | 470 public void onSSLStateUpdated(Tab tab) { |
380 updateUrlBar(); | 471 updateUrlBar(); |
381 } | 472 } |
382 | 473 |
383 @Override | 474 @Override |
384 public void onDidStartProvisionalLoadForFrame( | 475 public void onDidStartProvisionalLoadForFrame( |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
583 return new WebappDelegateFactory(this); | 674 return new WebappDelegateFactory(this); |
584 } | 675 } |
585 | 676 |
586 // We're temporarily disable CS on webapp since there are some issues. (http ://crbug.com/471950) | 677 // We're temporarily disable CS on webapp since there are some issues. (http ://crbug.com/471950) |
587 // TODO(changwan): re-enable it once the issues are resolved. | 678 // TODO(changwan): re-enable it once the issues are resolved. |
588 @Override | 679 @Override |
589 protected boolean isContextualSearchAllowed() { | 680 protected boolean isContextualSearchAllowed() { |
590 return false; | 681 return false; |
591 } | 682 } |
592 } | 683 } |
OLD | NEW |