chromium/base/test/android/javatests/src/org/chromium/base/test/transit/ActivityElement.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.base.test.transit;

import android.app.Activity;

import androidx.annotation.Nullable;

import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;

import java.util.List;

/**
 * Represents an {@link Activity} that needs to exist to consider the Station active.
 *
 * <p>Subclasses are treated as a different type.
 *
 * @param <ActivityT> exact type of Activity expected
 */
public class ActivityElement<ActivityT extends Activity> extends Element<ActivityT> {
    private final Class<ActivityT> mActivityClass;

    ActivityElement(Class<ActivityT> activityClass) {
        super("AE/" + activityClass.getCanonicalName());
        mActivityClass = activityClass;
    }

    @Override
    public ConditionWithResult<ActivityT> createEnterCondition() {
        return new ActivityExistsCondition();
    }

    @Override
    public @Nullable Condition createExitCondition() {
        return null;
    }

    private class ActivityExistsCondition extends ConditionWithResult<ActivityT> {

        public ActivityExistsCondition() {
            super(/* isRunOnUiThread= */ false);
        }

        @Override
        protected ConditionStatusWithResult<ActivityT> resolveWithSuppliers() {
            ActivityT candidate = null;
            List<Activity> allActivities = ApplicationStatus.getRunningActivities();
            for (Activity activity : allActivities) {
                if (mActivityClass.equals(activity.getClass())) {
                    ActivityT matched = mActivityClass.cast(activity);
                    if (candidate != null) {
                        return error("%s matched two Activities: %s, %s", this, candidate, matched)
                                .withoutResult();
                    }
                    candidate = matched;
                }
            }
            if (candidate == null) {
                return awaiting("No Activity with expected class").withoutResult();
            }

            @ActivityState int state = ApplicationStatus.getStateForActivity(candidate);
            String statusString =
                    String.format(
                            "matched: %s (state=%s)", candidate, activityStateDescription(state));
            if (state == ActivityState.RESUMED) {
                return fulfilled(statusString).withResult(candidate);
            } else {
                return awaiting(statusString).withoutResult();
            }
        }

        @Override
        public String buildDescription() {
            return "Activity exists and is RESUMED: " + mActivityClass.getSimpleName();
        }
    }

    private static String activityStateDescription(@ActivityState int state) {
        return switch (state) {
            case ActivityState.CREATED -> "CREATED";
            case ActivityState.STARTED -> "STARTED";
            case ActivityState.RESUMED -> "RESUMED";
            case ActivityState.PAUSED -> "PAUSED";
            case ActivityState.STOPPED -> "STOPPED";
            case ActivityState.DESTROYED -> "DESTROYED";
            default -> throw new IllegalStateException("Unexpected value: " + state);
        };
    }
}