Commit 3473f671 authored by Peter Kotwicz's avatar Peter Kotwicz Committed by Commit Bot

[Android WebAPK] Split out network error dialog logic into its own class 8/9

This CL splits out the network error dialog display logic into its own
class - WebApkSplashNetworkErrorObserver.java

This CL also:
- Renames WebappSplashScreenController#onFinishedNativeInit()
  to onShowWithNative() because the method is always called after show().
- Renames SameActivityWebappSplashDelegate#mOfflineDialog to
  #mWebApkOfflineDialog for clarity.

Change-Id: I8206dbe4c39efb06444cfda75139835d26fcff3a
Reviewed-on: https://chromium-review.googlesource.com/c/1405702
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#630660}
parent 6ffc4887
......@@ -38,9 +38,15 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
/** Whether native was loaded. Native must be loaded in order to record metrics. */
private boolean mNativeLoaded;
private Tab mTab;
private WebappInfo mWebappInfo;
private SameActivityWebappUmaCache mUmaCache;
private WebApkOfflineDialog mOfflineDialog;
private WebApkSplashNetworkErrorObserver mWebApkNetworkErrorObserver;
private WebApkOfflineDialog mWebApkOfflineDialog;
private static class SingleShotOnDrawListener implements ViewTreeObserver.OnDrawListener {
private final View mView;
......@@ -70,6 +76,7 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
@Override
public void showSplash(ViewGroup parentView, WebappInfo webappInfo) {
mParentView = parentView;
mWebappInfo = webappInfo;
mIsSplashVisible = true;
Context context = ContextUtils.getApplicationContext();
......@@ -102,8 +109,14 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
}
@Override
public void onNativeLoaded() {
public void showSplashWithNative(Tab tab) {
mNativeLoaded = true;
mTab = tab;
if (mWebappInfo.isForWebApk()) {
mWebApkNetworkErrorObserver =
new WebApkSplashNetworkErrorObserver(this, mWebappInfo.name());
mTab.addObserver(mWebApkNetworkErrorObserver);
}
if (mUmaCache != null) mUmaCache.commitMetrics();
}
......@@ -116,7 +129,13 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
@Override
public void run() {
mParentView.removeView(mSplashScreen);
if (mWebApkNetworkErrorObserver != null) {
mTab.removeObserver(mWebApkNetworkErrorObserver);
mWebApkNetworkErrorObserver = null;
}
finishSplashscreenTraceEvents();
mTab = null;
mSplashScreen = null;
finishedHidingCallback.run();
}
......@@ -134,21 +153,21 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
}
@Override
public boolean isNetworkErrorDialogVisible() {
return mOfflineDialog != null && mOfflineDialog.isShowing();
public boolean isWebApkNetworkErrorDialogVisible() {
return mWebApkOfflineDialog != null && mWebApkOfflineDialog.isShowing();
}
@Override
public void showNetworkErrorDialog(Tab tab, String errorMsg) {
mOfflineDialog = new WebApkOfflineDialog();
mOfflineDialog.show(tab.getActivity(), errorMsg);
public void showWebApkNetworkErrorDialog(String errorMsg) {
mWebApkOfflineDialog = new WebApkOfflineDialog();
mWebApkOfflineDialog.show(mTab.getActivity(), errorMsg);
}
@Override
public void hideNetworkErrorDialog() {
if (mOfflineDialog == null) return;
mOfflineDialog.cancel();
mOfflineDialog = null;
public void hideWebApkNetworkErrorDialog() {
if (mWebApkOfflineDialog == null) return;
mWebApkOfflineDialog.cancel();
mWebApkOfflineDialog = null;
}
/** Sets the splash screen layout and sets the splash screen's title and icon. */
......
// Copyright 2019 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.Context;
import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.metrics.WebApkUma;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.net.NetError;
import org.chromium.net.NetworkChangeNotifier;
/**
* Displays error dialog on top of splash screen if there is a network error while loading the
* start URL.
*/
public class WebApkSplashNetworkErrorObserver extends EmptyTabObserver {
// No error.
public static final int ERROR_OK = 0;
private WebappSplashDelegate mDelegate;
private String mWebApkName;
private boolean mDidShowNetworkErrorDialog;
/** Indicates whether reloading is allowed. */
private boolean mAllowReloads;
public WebApkSplashNetworkErrorObserver(WebappSplashDelegate delegate, String webApkName) {
mDelegate = delegate;
mWebApkName = webApkName;
}
@Override
public void onDidFinishNavigation(final Tab tab, final String url, boolean isInMainFrame,
boolean isErrorPage, boolean hasCommitted, boolean isSameDocument,
boolean isFragmentNavigation, Integer pageTransition, int errorCode,
int httpStatusCode) {
if (!isInMainFrame) return;
switch (errorCode) {
case ERROR_OK:
mDelegate.hideWebApkNetworkErrorDialog();
break;
case NetError.ERR_NETWORK_CHANGED:
onNetworkChanged(tab);
break;
default:
onNetworkError(tab, errorCode);
break;
}
WebApkUma.recordNetworkErrorWhenLaunch(-errorCode);
}
private void onNetworkChanged(Tab tab) {
if (!mAllowReloads) return;
// It is possible that we get {@link NetError.ERR_NETWORK_CHANGED} during the first
// reload after the device is online. The navigation will fail until the next auto
// reload fired by {@link NetErrorHelperCore}. We call reload explicitly to reduce the
// waiting time.
tab.reloadIgnoringCache();
mAllowReloads = false;
}
private void onNetworkError(final Tab tab, int errorCode) {
if (tab.getActivity() == null) return;
// Do not show the network error dialog more than once (e.g. if the user backed out of
// the dialog).
if (mDidShowNetworkErrorDialog) return;
mDidShowNetworkErrorDialog = true;
final NetworkChangeNotifier.ConnectionTypeObserver observer =
new NetworkChangeNotifier.ConnectionTypeObserver() {
@Override
public void onConnectionTypeChanged(int connectionType) {
if (!NetworkChangeNotifier.isOnline()) return;
NetworkChangeNotifier.removeConnectionTypeObserver(this);
tab.reloadIgnoringCache();
// One more reload is allowed after the network connection is back.
mAllowReloads = true;
}
};
NetworkChangeNotifier.addConnectionTypeObserver(observer);
mDelegate.showWebApkNetworkErrorDialog(generateNetworkErrorWebApkDialogMessage(errorCode));
}
/** Generates network error dialog message for the given error code. */
private String generateNetworkErrorWebApkDialogMessage(int errorCode) {
Context context = ContextUtils.getApplicationContext();
switch (errorCode) {
case NetError.ERR_INTERNET_DISCONNECTED:
return context.getString(R.string.webapk_offline_dialog, mWebApkName);
case NetError.ERR_TUNNEL_CONNECTION_FAILED:
return context.getString(
R.string.webapk_network_error_message_tunnel_connection_failed);
default:
return context.getString(R.string.webapk_cannot_connect_to_site);
}
}
}
......@@ -293,7 +293,14 @@ public class WebappActivity extends SingleTabActivity {
setTitle(mWebappInfo.shortName());
super.preInflationStartup();
initializeWebappData();
if (mWebappInfo.displayMode() == WebDisplayMode.FULLSCREEN) {
enterImmersiveMode();
}
try (TraceEvent te = TraceEvent.scoped("WebappActivity.showSplash")) {
ViewGroup contentView = (ViewGroup) findViewById(android.R.id.content);
mSplashController.showSplash(contentView, mWebappInfo);
}
}
@Override
......@@ -309,7 +316,7 @@ public class WebappActivity extends SingleTabActivity {
getToolbarManager().setCloseButtonDrawable(null); // Hides close button.
getFullscreenManager().setTab(getActivityTab());
mSplashController.onFinishedNativeInit(getActivityTab(), getCompositorViewHolder());
mSplashController.showSplashWithNative(getActivityTab(), getCompositorViewHolder());
super.finishNativeInitialization();
mIsInitialized = true;
}
......@@ -552,16 +559,6 @@ public class WebappActivity extends SingleTabActivity {
return Holder.sWebappInfoMap.remove(id);
}
private void initializeWebappData() {
try (TraceEvent te = TraceEvent.scoped("WebappActivity.initializeWebappData")) {
if (mWebappInfo.displayMode() == WebDisplayMode.FULLSCREEN) {
enterImmersiveMode();
}
ViewGroup contentView = (ViewGroup) findViewById(android.R.id.content);
mSplashController.showSplashScreen(contentView, mWebappInfo);
}
}
protected void updateStorage(WebappDataStorage storage) {
// The information in the WebappDataStorage may have been purged by the
// user clearing their history or not launching the web app recently.
......
......@@ -13,11 +13,8 @@ interface WebappSplashDelegate {
/** Shows the splash screen. */
void showSplash(ViewGroup parentView, WebappInfo webappInfo);
/**
* Called once native has been loaded. This may be called either before or after
* {@link #showSplash()}.
*/
void onNativeLoaded();
/** Called once native has been loaded and after {@link #showSplash()}. */
void showSplashWithNative(Tab tab);
/** Hides the splash screen. Runs the callback once the splash screen is hidden. */
void hideSplash(Runnable finishedHidingCallback);
......@@ -31,12 +28,12 @@ interface WebappSplashDelegate {
*/
ViewGroup getSplashViewIfChildOf(ViewGroup parent);
/** Returns whether the network error dialog is visible. */
boolean isNetworkErrorDialogVisible();
/** Returns whether the WebAPK network error dialog is visible. */
boolean isWebApkNetworkErrorDialogVisible();
/** Shows the network error dialog. */
void showNetworkErrorDialog(Tab tab, String errorMsg);
/** Shows the WebAPK network error dialog. */
void showWebApkNetworkErrorDialog(String errorMsg);
/** Hides the network error dialog. */
void hideNetworkErrorDialog();
/** Hides the WebAPK network error dialog. */
void hideWebApkNetworkErrorDialog();
}
......@@ -4,23 +4,17 @@
package org.chromium.chrome.browser.webapps;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.view.ViewGroup;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
import org.chromium.chrome.browser.metrics.WebApkUma;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.net.NetError;
import org.chromium.net.NetworkChangeNotifier;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
......@@ -43,9 +37,6 @@ public class WebappSplashScreenController extends EmptyTabObserver {
public static final String HISTOGRAM_SPLASHSCREEN_DURATION = "Webapp.Splashscreen.Duration";
public static final String HISTOGRAM_SPLASHSCREEN_HIDES = "Webapp.Splashscreen.Hides";
// No error.
public static final int ERROR_OK = 0;
private WebappSplashDelegate mDelegate;
/** Used to schedule splash screen hiding. */
......@@ -54,32 +45,21 @@ public class WebappSplashScreenController extends EmptyTabObserver {
/** View to which the splash screen is added. */
private ViewGroup mParentView;
/** Indicates whether reloading is allowed. */
private boolean mAllowReloads;
private String mAppName;
private boolean mIsForWebApk;
private boolean mDidShowNetworkErrorDialog;
/** Time that the splash screen was shown. */
private long mSplashShownTimestamp;
private ObserverList<SplashscreenObserver> mObservers;
public WebappSplashScreenController() {
mDelegate = new SameActivityWebappSplashDelegate();
mObservers = new ObserverList<>();
}
/** Shows the splash screen. */
public void showSplashScreen(ViewGroup parentView, final WebappInfo webappInfo) {
public void showSplash(ViewGroup parentView, final WebappInfo webappInfo) {
mParentView = parentView;
mIsForWebApk = webappInfo.isForWebApk();
mAppName = webappInfo.name();
mSplashShownTimestamp = SystemClock.elapsedRealtime();
mDelegate = new SameActivityWebappSplashDelegate();
mDelegate.showSplash(parentView, webappInfo);
notifySplashscreenVisible(mSplashShownTimestamp);
......@@ -97,15 +77,16 @@ public class WebappSplashScreenController extends EmptyTabObserver {
}
}
/** Should be called once native has loaded. */
public void onFinishedNativeInit(Tab tab, CompositorViewHolder compositorViewHolder) {
/** Should be called once native has loaded and after {@link #showSplash()}. */
public void showSplashWithNative(Tab tab, CompositorViewHolder compositorViewHolder) {
mCompositorViewHolder = compositorViewHolder;
tab.addObserver(this);
mDelegate.onNativeLoaded();
mDelegate.showSplashWithNative(tab);
}
@VisibleForTesting
ViewGroup getSplashScreenForTests() {
if (mDelegate == null) return null;
return mDelegate.getSplashViewIfChildOf(mParentView);
}
......@@ -135,80 +116,8 @@ public class WebappSplashScreenController extends EmptyTabObserver {
hideSplash(tab, SplashHidesReason.CRASH);
}
@Override
public void onDidFinishNavigation(final Tab tab, final String url, boolean isInMainFrame,
boolean isErrorPage, boolean hasCommitted, boolean isSameDocument,
boolean isFragmentNavigation, Integer pageTransition, int errorCode,
int httpStatusCode) {
if (!mIsForWebApk || !isInMainFrame) return;
switch (errorCode) {
case ERROR_OK:
mDelegate.hideNetworkErrorDialog();
break;
case NetError.ERR_NETWORK_CHANGED:
onNetworkChanged(tab);
break;
default:
onNetworkError(tab, errorCode);
break;
}
WebApkUma.recordNetworkErrorWhenLaunch(-errorCode);
}
protected boolean canHideSplashScreen() {
return !mDelegate.isNetworkErrorDialogVisible();
}
private void onNetworkChanged(Tab tab) {
if (!mAllowReloads) return;
// It is possible that we get {@link NetError.ERR_NETWORK_CHANGED} during the first
// reload after the device is online. The navigation will fail until the next auto
// reload fired by {@link NetErrorHelperCore}. We call reload explicitly to reduce the
// waiting time.
tab.reloadIgnoringCache();
mAllowReloads = false;
}
private void onNetworkError(final Tab tab, int errorCode) {
if (tab.getActivity() == null) return;
// Do not show the network error dialog more than once (e.g. if the user backed out of
// the dialog).
if (mDidShowNetworkErrorDialog) return;
mDidShowNetworkErrorDialog = true;
final NetworkChangeNotifier.ConnectionTypeObserver observer =
new NetworkChangeNotifier.ConnectionTypeObserver() {
@Override
public void onConnectionTypeChanged(int connectionType) {
if (!NetworkChangeNotifier.isOnline()) return;
NetworkChangeNotifier.removeConnectionTypeObserver(this);
tab.reloadIgnoringCache();
// One more reload is allowed after the network connection is back.
mAllowReloads = true;
}
};
NetworkChangeNotifier.addConnectionTypeObserver(observer);
mDelegate.showNetworkErrorDialog(tab, generateNetworkErrorWebApkDialogMessage(errorCode));
}
/** Generates network error dialog message for the given error code. */
private String generateNetworkErrorWebApkDialogMessage(int errorCode) {
Context context = ContextUtils.getApplicationContext();
switch (errorCode) {
case NetError.ERR_INTERNET_DISCONNECTED:
return context.getString(R.string.webapk_offline_dialog, mAppName);
case NetError.ERR_TUNNEL_CONNECTION_FAILED:
return context.getString(
R.string.webapk_network_error_message_tunnel_connection_failed);
default:
return context.getString(R.string.webapk_cannot_connect_to_site);
}
return !mDelegate.isWebApkNetworkErrorDialogVisible();
}
/** Hides the splash screen. */
......@@ -220,6 +129,7 @@ public class WebappSplashScreenController extends EmptyTabObserver {
public void run() {
tab.removeObserver(WebappSplashScreenController.this);
mCompositorViewHolder = null;
mDelegate = null;
long splashHiddenTimestamp = SystemClock.elapsedRealtime();
notifySplashscreenHidden(splashHiddenTimestamp);
......@@ -235,7 +145,7 @@ public class WebappSplashScreenController extends EmptyTabObserver {
// Without this callback we were seeing a short flash of white between the splash screen and
// the web content (crbug.com/734500).
mCompositorViewHolder.getCompositorView().surfaceRedrawNeededAsync(() -> {
if (!mDelegate.isSplashVisible()) return;
if (mDelegate != null && !mDelegate.isSplashVisible()) return;
mDelegate.hideSplash(onHiddenCallback);
});
}
......
......@@ -1725,6 +1725,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkSplashNetworkErrorObserver.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkUpdateDataFetcher.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkUpdateTask.java",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment