chromium/chrome/android/java/src/org/chromium/chrome/browser/app/download/home/DownloadActivityLauncher.java

// Copyright 2024 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.app.download.home;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.IntentUtils;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.profiles.OTRProfileID;
import org.chromium.ui.modaldialog.ModalDialogManagerHolder;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;

/**
 * Class for launching the DownloadActivity and monitoring its status so dialogs could be shown on
 * top of it.
 */
public class DownloadActivityLauncher implements ApplicationStatus.ActivityStateListener {
    private static DownloadActivityLauncher sInstance;
    private static final String EXTRA_SHOW_PREFETCHED_CONTENT =
            "org.chromium.chrome.browser.download.SHOW_PREFETCHED_CONTENT";
    private @DownloadActivityStatus int mActivityStatus = DownloadActivityStatus.NOT_CREATED;
    private List<Callback<Activity>> mActivityCallbacks = new ArrayList();

    /** A set of states that represent the last state change of an Activity. */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
        DownloadActivityStatus.NOT_CREATED,
        DownloadActivityStatus.INITIALIZING,
        DownloadActivityStatus.STARTED,
    })
    public @interface DownloadActivityStatus {
        /** DownloadActivity is not created. */
        int NOT_CREATED = 0;

        /** StartActivity() has been called. */
        int INITIALIZING = 1;

        /** OnResume() is called after StartActivity(). */
        int STARTED = 3;
    }

    /**
     * Returns the singleton instance, lazily creating one if needed.
     *
     * @return The singleton instance.
     */
    public static DownloadActivityLauncher getInstance() {
        ThreadUtils.assertOnUiThread();

        if (sInstance == null) {
            sInstance = new DownloadActivityLauncher();
        }
        return sInstance;
    }

    /**
     * Launches the download activity on phones.
     *
     * @param activity The current activity is available.
     * @param otrProfileID The {@link OTRProfileID} to determine whether download home should be
     *     opened in incognito mode. Only used when no valid current or recent tab presents.
     * @param showPrefetchedContent Whether the manager should start with prefetched content section
     *     expanded.
     */
    public void showDownloadActivity(
            @Nullable Activity activity,
            @Nullable OTRProfileID otrProfileID,
            boolean showPrefetchedContent) {
        Context appContext = ContextUtils.getApplicationContext();

        Intent intent = new Intent();
        intent.setClass(appContext, DownloadActivity.class);
        intent.putExtra(EXTRA_SHOW_PREFETCHED_CONTENT, showPrefetchedContent);
        if (otrProfileID != null) {
            intent.putExtra(
                    DownloadUtils.EXTRA_OTR_PROFILE_ID, OTRProfileID.serialize(otrProfileID));
        }

        if (activity == null) {
            // Stands alone in its own task.
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            appContext.startActivity(intent);
        } else {
            // Sits on top of another Activity.
            intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
            activity.startActivity(intent);
        }
        // Start tracking the DownloadActivity status.
        if (mActivityStatus == DownloadActivityStatus.NOT_CREATED) {
            mActivityStatus = DownloadActivityStatus.INITIALIZING;
            ApplicationStatus.registerStateListenerForAllActivities(this);
        }
    }

    /**
     * @return Whether or not the prefetched content section should be expanded on launch of the
     *     DownloadActivity.
     */
    public static boolean shouldShowPrefetchContent(Intent intent) {
        return IntentUtils.safeGetBooleanExtra(intent, EXTRA_SHOW_PREFETCHED_CONTENT, false);
    }

    @Override
    public void onActivityStateChange(Activity activity, @ActivityState int newState) {
        if (!(activity instanceof DownloadActivity)) return;
        if (newState == ActivityState.RESUMED) {
            mActivityStatus = DownloadActivityStatus.STARTED;
            for (Callback<Activity> callback : mActivityCallbacks) {
                callback.onResult(activity);
            }
            mActivityCallbacks.clear();
        } else if (newState == ActivityState.DESTROYED) {
            mActivityStatus = DownloadActivityStatus.NOT_CREATED;
            ApplicationStatus.unregisterActivityStateListener(this);
        }
    }

    public void getActivityForOpenDialog(Callback<Activity> callback) {
        if (mActivityStatus != DownloadActivityStatus.INITIALIZING) {
            Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
            if (activity instanceof ModalDialogManagerHolder) {
                callback.onResult(activity);
            } else {
                mActivityCallbacks.add(callback);
            }
        } else {
            mActivityCallbacks.add(callback);
        }
    }
}