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 { ...@@ -38,9 +38,15 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
/** Whether native was loaded. Native must be loaded in order to record metrics. */ /** Whether native was loaded. Native must be loaded in order to record metrics. */
private boolean mNativeLoaded; private boolean mNativeLoaded;
private Tab mTab;
private WebappInfo mWebappInfo;
private SameActivityWebappUmaCache mUmaCache; private SameActivityWebappUmaCache mUmaCache;
private WebApkOfflineDialog mOfflineDialog; private WebApkSplashNetworkErrorObserver mWebApkNetworkErrorObserver;
private WebApkOfflineDialog mWebApkOfflineDialog;
private static class SingleShotOnDrawListener implements ViewTreeObserver.OnDrawListener { private static class SingleShotOnDrawListener implements ViewTreeObserver.OnDrawListener {
private final View mView; private final View mView;
...@@ -70,6 +76,7 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate { ...@@ -70,6 +76,7 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
@Override @Override
public void showSplash(ViewGroup parentView, WebappInfo webappInfo) { public void showSplash(ViewGroup parentView, WebappInfo webappInfo) {
mParentView = parentView; mParentView = parentView;
mWebappInfo = webappInfo;
mIsSplashVisible = true; mIsSplashVisible = true;
Context context = ContextUtils.getApplicationContext(); Context context = ContextUtils.getApplicationContext();
...@@ -102,8 +109,14 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate { ...@@ -102,8 +109,14 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
} }
@Override @Override
public void onNativeLoaded() { public void showSplashWithNative(Tab tab) {
mNativeLoaded = true; mNativeLoaded = true;
mTab = tab;
if (mWebappInfo.isForWebApk()) {
mWebApkNetworkErrorObserver =
new WebApkSplashNetworkErrorObserver(this, mWebappInfo.name());
mTab.addObserver(mWebApkNetworkErrorObserver);
}
if (mUmaCache != null) mUmaCache.commitMetrics(); if (mUmaCache != null) mUmaCache.commitMetrics();
} }
...@@ -116,7 +129,13 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate { ...@@ -116,7 +129,13 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
@Override @Override
public void run() { public void run() {
mParentView.removeView(mSplashScreen); mParentView.removeView(mSplashScreen);
if (mWebApkNetworkErrorObserver != null) {
mTab.removeObserver(mWebApkNetworkErrorObserver);
mWebApkNetworkErrorObserver = null;
}
finishSplashscreenTraceEvents(); finishSplashscreenTraceEvents();
mTab = null;
mSplashScreen = null; mSplashScreen = null;
finishedHidingCallback.run(); finishedHidingCallback.run();
} }
...@@ -134,21 +153,21 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate { ...@@ -134,21 +153,21 @@ public class SameActivityWebappSplashDelegate implements WebappSplashDelegate {
} }
@Override @Override
public boolean isNetworkErrorDialogVisible() { public boolean isWebApkNetworkErrorDialogVisible() {
return mOfflineDialog != null && mOfflineDialog.isShowing(); return mWebApkOfflineDialog != null && mWebApkOfflineDialog.isShowing();
} }
@Override @Override
public void showNetworkErrorDialog(Tab tab, String errorMsg) { public void showWebApkNetworkErrorDialog(String errorMsg) {
mOfflineDialog = new WebApkOfflineDialog(); mWebApkOfflineDialog = new WebApkOfflineDialog();
mOfflineDialog.show(tab.getActivity(), errorMsg); mWebApkOfflineDialog.show(mTab.getActivity(), errorMsg);
} }
@Override @Override
public void hideNetworkErrorDialog() { public void hideWebApkNetworkErrorDialog() {
if (mOfflineDialog == null) return; if (mWebApkOfflineDialog == null) return;
mOfflineDialog.cancel(); mWebApkOfflineDialog.cancel();
mOfflineDialog = null; mWebApkOfflineDialog = null;
} }
/** Sets the splash screen layout and sets the splash screen's title and icon. */ /** 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 { ...@@ -293,7 +293,14 @@ public class WebappActivity extends SingleTabActivity {
setTitle(mWebappInfo.shortName()); setTitle(mWebappInfo.shortName());
super.preInflationStartup(); 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 @Override
...@@ -309,7 +316,7 @@ public class WebappActivity extends SingleTabActivity { ...@@ -309,7 +316,7 @@ public class WebappActivity extends SingleTabActivity {
getToolbarManager().setCloseButtonDrawable(null); // Hides close button. getToolbarManager().setCloseButtonDrawable(null); // Hides close button.
getFullscreenManager().setTab(getActivityTab()); getFullscreenManager().setTab(getActivityTab());
mSplashController.onFinishedNativeInit(getActivityTab(), getCompositorViewHolder()); mSplashController.showSplashWithNative(getActivityTab(), getCompositorViewHolder());
super.finishNativeInitialization(); super.finishNativeInitialization();
mIsInitialized = true; mIsInitialized = true;
} }
...@@ -552,16 +559,6 @@ public class WebappActivity extends SingleTabActivity { ...@@ -552,16 +559,6 @@ public class WebappActivity extends SingleTabActivity {
return Holder.sWebappInfoMap.remove(id); 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) { protected void updateStorage(WebappDataStorage storage) {
// The information in the WebappDataStorage may have been purged by the // The information in the WebappDataStorage may have been purged by the
// user clearing their history or not launching the web app recently. // user clearing their history or not launching the web app recently.
......
...@@ -13,11 +13,8 @@ interface WebappSplashDelegate { ...@@ -13,11 +13,8 @@ interface WebappSplashDelegate {
/** Shows the splash screen. */ /** Shows the splash screen. */
void showSplash(ViewGroup parentView, WebappInfo webappInfo); void showSplash(ViewGroup parentView, WebappInfo webappInfo);
/** /** Called once native has been loaded and after {@link #showSplash()}. */
* Called once native has been loaded. This may be called either before or after void showSplashWithNative(Tab tab);
* {@link #showSplash()}.
*/
void onNativeLoaded();
/** Hides the splash screen. Runs the callback once the splash screen is hidden. */ /** Hides the splash screen. Runs the callback once the splash screen is hidden. */
void hideSplash(Runnable finishedHidingCallback); void hideSplash(Runnable finishedHidingCallback);
...@@ -31,12 +28,12 @@ interface WebappSplashDelegate { ...@@ -31,12 +28,12 @@ interface WebappSplashDelegate {
*/ */
ViewGroup getSplashViewIfChildOf(ViewGroup parent); ViewGroup getSplashViewIfChildOf(ViewGroup parent);
/** Returns whether the network error dialog is visible. */ /** Returns whether the WebAPK network error dialog is visible. */
boolean isNetworkErrorDialogVisible(); boolean isWebApkNetworkErrorDialogVisible();
/** Shows the network error dialog. */ /** Shows the WebAPK network error dialog. */
void showNetworkErrorDialog(Tab tab, String errorMsg); void showWebApkNetworkErrorDialog(String errorMsg);
/** Hides the network error dialog. */ /** Hides the WebAPK network error dialog. */
void hideNetworkErrorDialog(); void hideWebApkNetworkErrorDialog();
} }
...@@ -4,23 +4,17 @@ ...@@ -4,23 +4,17 @@
package org.chromium.chrome.browser.webapps; package org.chromium.chrome.browser.webapps;
import android.content.Context;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList; import org.chromium.base.ObserverList;
import org.chromium.base.VisibleForTesting; import org.chromium.base.VisibleForTesting;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.WarmupManager; import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.compositor.CompositorViewHolder; 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.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; 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.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -43,9 +37,6 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -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_DURATION = "Webapp.Splashscreen.Duration";
public static final String HISTOGRAM_SPLASHSCREEN_HIDES = "Webapp.Splashscreen.Hides"; public static final String HISTOGRAM_SPLASHSCREEN_HIDES = "Webapp.Splashscreen.Hides";
// No error.
public static final int ERROR_OK = 0;
private WebappSplashDelegate mDelegate; private WebappSplashDelegate mDelegate;
/** Used to schedule splash screen hiding. */ /** Used to schedule splash screen hiding. */
...@@ -54,32 +45,21 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -54,32 +45,21 @@ public class WebappSplashScreenController extends EmptyTabObserver {
/** View to which the splash screen is added. */ /** View to which the splash screen is added. */
private ViewGroup mParentView; 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. */ /** Time that the splash screen was shown. */
private long mSplashShownTimestamp; private long mSplashShownTimestamp;
private ObserverList<SplashscreenObserver> mObservers; private ObserverList<SplashscreenObserver> mObservers;
public WebappSplashScreenController() { public WebappSplashScreenController() {
mDelegate = new SameActivityWebappSplashDelegate();
mObservers = new ObserverList<>(); mObservers = new ObserverList<>();
} }
/** Shows the splash screen. */ /** Shows the splash screen. */
public void showSplashScreen(ViewGroup parentView, final WebappInfo webappInfo) { public void showSplash(ViewGroup parentView, final WebappInfo webappInfo) {
mParentView = parentView; mParentView = parentView;
mIsForWebApk = webappInfo.isForWebApk();
mAppName = webappInfo.name();
mSplashShownTimestamp = SystemClock.elapsedRealtime(); mSplashShownTimestamp = SystemClock.elapsedRealtime();
mDelegate = new SameActivityWebappSplashDelegate();
mDelegate.showSplash(parentView, webappInfo); mDelegate.showSplash(parentView, webappInfo);
notifySplashscreenVisible(mSplashShownTimestamp); notifySplashscreenVisible(mSplashShownTimestamp);
...@@ -97,15 +77,16 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -97,15 +77,16 @@ public class WebappSplashScreenController extends EmptyTabObserver {
} }
} }
/** Should be called once native has loaded. */ /** Should be called once native has loaded and after {@link #showSplash()}. */
public void onFinishedNativeInit(Tab tab, CompositorViewHolder compositorViewHolder) { public void showSplashWithNative(Tab tab, CompositorViewHolder compositorViewHolder) {
mCompositorViewHolder = compositorViewHolder; mCompositorViewHolder = compositorViewHolder;
tab.addObserver(this); tab.addObserver(this);
mDelegate.onNativeLoaded(); mDelegate.showSplashWithNative(tab);
} }
@VisibleForTesting @VisibleForTesting
ViewGroup getSplashScreenForTests() { ViewGroup getSplashScreenForTests() {
if (mDelegate == null) return null;
return mDelegate.getSplashViewIfChildOf(mParentView); return mDelegate.getSplashViewIfChildOf(mParentView);
} }
...@@ -135,80 +116,8 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -135,80 +116,8 @@ public class WebappSplashScreenController extends EmptyTabObserver {
hideSplash(tab, SplashHidesReason.CRASH); 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() { protected boolean canHideSplashScreen() {
return !mDelegate.isNetworkErrorDialogVisible(); return !mDelegate.isWebApkNetworkErrorDialogVisible();
}
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);
}
} }
/** Hides the splash screen. */ /** Hides the splash screen. */
...@@ -220,6 +129,7 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -220,6 +129,7 @@ public class WebappSplashScreenController extends EmptyTabObserver {
public void run() { public void run() {
tab.removeObserver(WebappSplashScreenController.this); tab.removeObserver(WebappSplashScreenController.this);
mCompositorViewHolder = null; mCompositorViewHolder = null;
mDelegate = null;
long splashHiddenTimestamp = SystemClock.elapsedRealtime(); long splashHiddenTimestamp = SystemClock.elapsedRealtime();
notifySplashscreenHidden(splashHiddenTimestamp); notifySplashscreenHidden(splashHiddenTimestamp);
...@@ -235,7 +145,7 @@ public class WebappSplashScreenController extends EmptyTabObserver { ...@@ -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 // Without this callback we were seeing a short flash of white between the splash screen and
// the web content (crbug.com/734500). // the web content (crbug.com/734500).
mCompositorViewHolder.getCompositorView().surfaceRedrawNeededAsync(() -> { mCompositorViewHolder.getCompositorView().surfaceRedrawNeededAsync(() -> {
if (!mDelegate.isSplashVisible()) return; if (mDelegate != null && !mDelegate.isSplashVisible()) return;
mDelegate.hideSplash(onHiddenCallback); mDelegate.hideSplash(onHiddenCallback);
}); });
} }
......
...@@ -1725,6 +1725,7 @@ chrome_java_sources = [ ...@@ -1725,6 +1725,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/webapps/WebApkPostShareTargetNavigator.java", "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/WebApkServiceClient.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkShareTargetUtil.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/WebApkUpdateDataFetcher.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java", "java/src/org/chromium/chrome/browser/webapps/WebApkUpdateManager.java",
"java/src/org/chromium/chrome/browser/webapps/WebApkUpdateTask.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