chromium/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkActivityLifecycleUmaTracker.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 static org.chromium.chrome.browser.dependency_injection.ChromeCommonQualifiers.SAVED_INSTANCE_SUPPLIER;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;

import dagger.Lazy;

import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.ApplicationStatus.ActivityStateListener;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.browserservices.intents.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.intents.WebApkExtras;
import org.chromium.chrome.browser.browserservices.intents.WebappIntentUtils;
import org.chromium.chrome.browser.browserservices.metrics.WebApkUkmRecorder;
import org.chromium.chrome.browser.browserservices.metrics.WebApkUmaRecorder;
import org.chromium.chrome.browser.browserservices.ui.splashscreen.SplashController;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.flags.ActivityType;
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.metrics.LegacyTabStartupMetricsTracker;
import org.chromium.chrome.browser.metrics.StartupMetricsTracker;
import org.chromium.chrome.browser.metrics.WebApkSplashscreenMetrics;

import javax.inject.Inject;
import javax.inject.Named;

/** Handles recording user metrics for WebAPK activities. */
@ActivityScope
public class WebApkActivityLifecycleUmaTracker
        implements ActivityStateListener, InflationObserver, PauseResumeWithNativeObserver {
    private final Activity mActivity;
    private final BrowserServicesIntentDataProvider mIntentDataProvider;
    private final SplashController mSplashController;
    private final Lazy<LegacyTabStartupMetricsTracker> mLegacyTabStartupMetricsTracker;
    private final Lazy<StartupMetricsTracker> mStartupMetricsTracker;
    private final Supplier<Bundle> mSavedInstanceStateSupplier;

    /** The start time that the activity becomes focused in milliseconds since boot. */
    private long mStartTime;

    @Inject
    public WebApkActivityLifecycleUmaTracker(
            Activity activity,
            BrowserServicesIntentDataProvider intentDataProvider,
            SplashController splashController,
            ActivityLifecycleDispatcher lifecycleDispatcher,
            WebappDeferredStartupWithStorageHandler deferredStartupWithStorageHandler,
            Lazy<LegacyTabStartupMetricsTracker> legacyStartupMetricsTracker,
            Lazy<StartupMetricsTracker> startupMetricsTracker,
            @Named(SAVED_INSTANCE_SUPPLIER) Supplier<Bundle> savedInstanceStateSupplier) {
        mActivity = activity;
        mIntentDataProvider = intentDataProvider;
        mSplashController = splashController;
        mLegacyTabStartupMetricsTracker = legacyStartupMetricsTracker;
        mStartupMetricsTracker = startupMetricsTracker;
        mSavedInstanceStateSupplier = savedInstanceStateSupplier;

        lifecycleDispatcher.register(this);
        ApplicationStatus.registerStateListenerForActivity(this, mActivity);

        // Add UMA recording task at the front of the deferred startup queue as it has a higher
        // priority than other deferred startup tasks like checking for a WebAPK update.
        deferredStartupWithStorageHandler.addTaskToFront(
                (storage, didCreateStorage) -> {
                    if (lifecycleDispatcher.isActivityFinishingOrDestroyed()) return;

                    WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
                    WebApkUmaRecorder.recordShellApkVersion(
                            webApkExtras.shellApkVersion, webApkExtras.distributor);
                });
    }

    @Override
    public void onActivityStateChange(Activity activity, @ActivityState int newState) {
        if (newState == ActivityState.RESUMED) {
            mStartTime = SystemClock.elapsedRealtime();
        }
    }

    @Override
    public void onPreInflationStartup() {
        // Decide whether to record startup UMA histograms. This is a similar check to the one done
        // in ChromeTabbedActivity.performPreInflationStartup refer to the comment there for why.
        if (!LibraryLoader.getInstance().isInitialized()) {
            mLegacyTabStartupMetricsTracker.get().setHistogramSuffix(ActivityType.WEB_APK);
            mStartupMetricsTracker.get().setHistogramSuffix(ActivityType.WEB_APK);
            // If there is a saved instance state, then the intent (and its stored timestamp) might
            // be stale (Android replays intents if there is a recents entry for the activity).
            if (mSavedInstanceStateSupplier.get() == null) {
                Intent intent = mActivity.getIntent();
                // Splash observers are removed once the splash screen is hidden.
                mSplashController.addObserver(
                        new WebApkSplashscreenMetrics(
                                WebappIntentUtils.getWebApkShellLaunchTime(intent),
                                WebappIntentUtils.getNewStyleWebApkSplashShownTime(intent)));
            }
        }
    }

    @Override
    public void onPostInflationStartup() {}

    @Override
    public void onResumeWithNative() {
    }

    @Override
    public void onPauseWithNative() {
        WebApkExtras webApkExtras = mIntentDataProvider.getWebApkExtras();
        long sessionDuration = SystemClock.elapsedRealtime() - mStartTime;
        WebApkUmaRecorder.recordWebApkSessionDuration(webApkExtras.distributor, sessionDuration);
        WebApkUkmRecorder.recordWebApkSessionDuration(
                webApkExtras.manifestId,
                webApkExtras.distributor,
                webApkExtras.webApkVersionCode,
                sessionDuration);
    }
}