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.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 |