chromium/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkSplashNetworkErrorObserver.java

// Copyright 2019 The Chromium Authors
// 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.app.Activity;
import android.content.Context;

import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.browserservices.metrics.WebApkUmaRecorder;
import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.content_public.browser.NavigationHandle;
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 {
    private Activity mActivity;
    private WebApkOfflineDialog mOfflineDialog;
    private String mWebApkName;

    private boolean mDidShowNetworkErrorDialog;

    /** Indicates whether reloading is allowed. */
    private boolean mAllowReloads;

    public WebApkSplashNetworkErrorObserver(Activity activity, String webApkName) {
        mActivity = activity;
        mWebApkName = webApkName;
    }

    public boolean isNetworkErrorDialogVisible() {
        return mOfflineDialog != null && mOfflineDialog.isShowing();
    }

    @Override
    public void onDidFinishNavigationInPrimaryMainFrame(
            final Tab tab, NavigationHandle navigation) {
        switch (navigation.errorCode()) {
            case NetError.OK:
                if (mOfflineDialog != null) {
                    mOfflineDialog.cancel();
                    mOfflineDialog = null;
                }
                break;
            case NetError.ERR_NETWORK_CHANGED:
                onNetworkChanged(tab);
                break;
            default:
                String dialogMessage =
                        generateNetworkErrorWebApkDialogMessage(navigation.errorCode());
                if (dialogMessage != null) {
                    onNetworkError(tab, dialogMessage);
                }
                break;
        }
        WebApkUmaRecorder.recordNetworkErrorWhenLaunch(-navigation.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, String dialogMessage) {
        // 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);
        mOfflineDialog = new WebApkOfflineDialog();
        mOfflineDialog.show(mActivity, dialogMessage);
    }

    /**
     * Generates network error dialog message for the given error code. Returns null if the
     * dialog should not be shown.
     */
    private String generateNetworkErrorWebApkDialogMessage(@NetError int errorCode) {
        Context context = ContextUtils.getApplicationContext();
        switch (errorCode) {
            case NetError.ERR_INTERNET_DISCONNECTED:
                return null;
            case NetError.ERR_TUNNEL_CONNECTION_FAILED:
                return context.getString(
                        R.string.webapk_network_error_message_tunnel_connection_failed);
            case NetError.ERR_NAME_NOT_RESOLVED:
                return context.getString(R.string.webapk_cannot_connect_to_site);
            default:
                return null;
        }
    }
}