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

// Copyright 2020 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 androidx.annotation.NonNull;

import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.intents.WebappInfo;
import org.chromium.chrome.browser.browserservices.ui.SharedActivityCoordinator;
import org.chromium.chrome.browser.browserservices.ui.controller.CurrentPageVerifier;
import org.chromium.chrome.browser.browserservices.ui.splashscreen.webapps.WebappSplashController;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.lifecycle.InflationObserver;
import org.chromium.chrome.browser.lifecycle.PauseResumeWithNativeObserver;
import org.chromium.chrome.browser.lifecycle.StartStopWithNativeObserver;
import org.chromium.chrome.browser.metrics.LaunchMetrics;
import org.chromium.chrome.browser.util.AndroidTaskUtils;

import javax.inject.Inject;

/**
 * Coordinator shared between webapp activity and WebAPK activity components.
 * Add methods here if other components need to communicate with either of these components.
 */
@ActivityScope
public class WebappActivityCoordinator
        implements InflationObserver, PauseResumeWithNativeObserver, StartStopWithNativeObserver {
    private final BrowserServicesIntentDataProvider mIntentDataProvider;
    private final WebappInfo mWebappInfo;
    private final Activity mActivity;
    private final WebappDeferredStartupWithStorageHandler mDeferredStartupWithStorageHandler;

    // Whether the current page is within the webapp's scope.

    @Inject
    public WebappActivityCoordinator(
            SharedActivityCoordinator sharedActivityCoordinator,
            Activity activity,
            BrowserServicesIntentDataProvider intentDataProvider,
            ActivityTabProvider activityTabProvider,
            CurrentPageVerifier currentPageVerifier,
            WebappSplashController splashController,
            WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
            WebappActionsNotificationManager actionsNotificationManager,
            ActivityLifecycleDispatcher lifecycleDispatcher) {
        // We don't need to do anything with |sharedActivityCoordinator|, |splashController| or
        // |actionsNotificationManager|. We just need to resolve it so that it starts working.

        mIntentDataProvider = intentDataProvider;
        mWebappInfo = WebappInfo.create(mIntentDataProvider);
        mActivity = activity;
        mDeferredStartupWithStorageHandler = deferredStartupWithStorageHandler;

        mDeferredStartupWithStorageHandler.addTask(
                (storage, didCreateStorage) -> {
                    if (lifecycleDispatcher.isActivityFinishingOrDestroyed()) return;

                    if (storage != null) {
                        updateStorage(storage);
                    }
                });

        lifecycleDispatcher.register(this);

        // Initialize the WebappRegistry and warm up the shared preferences for this web app. No-ops
        // if the registry and this web app are already initialized. Must override Strict Mode to
        // avoid a violation.
        WebappRegistry.getInstance();
        WebappRegistry.warmUpSharedPrefsForId(mWebappInfo.id());
    }

    /** Invoked to add deferred startup tasks to queue. */
    public void initDeferredStartupForActivity() {
        mDeferredStartupWithStorageHandler.initDeferredStartupForActivity();
    }

    @Override
    public void onPreInflationStartup() {
        LaunchMetrics.recordHomeScreenLaunchIntoStandaloneActivity(mWebappInfo);
    }

    @Override
    public void onPostInflationStartup() {}

    @Override
    public void onStartWithNative() {
        WebappDirectoryManager.cleanUpDirectories();
    }

    @Override
    public void onStopWithNative() {}

    @Override
    public void onPauseWithNative() {}

    @Override
    public void onResumeWithNative() {
        if (!mActivity.isFinishing() && mActivity.getIntent() != null) {
            // Avoid situations where Android starts two Activities with the same data.
            // TODO: Determine whether this is still needed now that we use
            // Activity#finishAndRemoveTask() when handling 'back'.
            AndroidTaskUtils.finishOtherTasksWithData(
                    mActivity.getIntent().getData(), mActivity.getTaskId());
        }
    }

    private void updateStorage(@NonNull WebappDataStorage storage) {
        // The information in the WebappDataStorage may have been purged by the
        // user clearing their history or not launching the web app recently.
        // Restore the data if necessary.
        storage.updateFromWebappIntentDataProvider(mIntentDataProvider);

        // A recent last used time is the indicator that the web app is still
        // present on the home screen, and enables sources such as notifications to
        // launch web apps. Thus, we do not update the last used time when the web
        // app is not directly launched from the home screen, as this interferes
        // with the heuristic.
        if (mWebappInfo.isLaunchedFromHomescreen()) {
            // TODO(yusufo): WebappRegistry#unregisterOldWebapps uses this information to delete
            // WebappDataStorage objects for legacy webapps which haven't been used in a while.
            storage.updateLastUsedTime();
        }
    }

    /**
     * @return the {@link WebappInfo} for the WebappActivity.
     */
    public WebappInfo getWebappInfo() {
        return mWebappInfo;
    }
}