chromium/base/test/android/javatests/src/org/chromium/base/test/transit/FragmentElement.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 androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;

import java.util.List;

/**
 * Represents a fragment, which needs to exist to consider the Station or Facility active.
 *
 * @param <FragmentT> the exact type of fragment expected.
 * @param <ActivityT> the {@link ActivityElement} corresponding to the owning activity.
 */
public class FragmentElement<FragmentT extends Fragment, ActivityT extends FragmentActivity>
        extends Element<FragmentT> {
    private ActivityElement<ActivityT> mActivityElement;
    private Class<FragmentT> mFragmentClass;

    /**
     * Constructor.
     *
     * @param fragmentClass the exact type of the fragment expected to appear in the view.
     * @param activityElement the element for the activity owning the fragment.
     */
    public FragmentElement(
            Class<FragmentT> fragmentClass, ActivityElement<ActivityT> activityElement) {
        super("FE/" + fragmentClass.getCanonicalName());
        mActivityElement = activityElement;
        mFragmentClass = fragmentClass;
    }

    @Override
    public ConditionWithResult<FragmentT> createEnterCondition() {
        return new FragmentExistsCondition();
    }

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

    private class FragmentExistsCondition extends ConditionWithResult<FragmentT> {
        public FragmentExistsCondition() {
            super(/* isRunOnUiThread= */ false);
        }

        @Override
        public String buildDescription() {
            return "Fragment exists and visible: " + mFragmentClass.getSimpleName();
        }

        @Override
        protected ConditionStatusWithResult<FragmentT> resolveWithSuppliers() throws Exception {
            ActivityT activity = mActivityElement.get();
            if (activity == null) return awaiting("No activity found").withoutResult();
            List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
            FragmentT candidate = null;
            for (Fragment fragment : fragments) {
                if (mFragmentClass.equals(fragment.getClass())) {
                    FragmentT matched = mFragmentClass.cast(fragment);
                    if (candidate != null) {
                        return error("%s matched two Fragments: %s, %s", this, candidate, matched)
                                .withoutResult();
                    }
                    candidate = matched;
                }
            }
            if (candidate == null) {
                return awaiting("No fragment found").withoutResult();
            }
            if (!candidate.isVisible()) {
                return awaiting("Fragment is not visible").withoutResult();
            }
            return fulfilled("Matched fragment: %s", candidate).withResult(candidate);
        }
    }

    private class FragmentNotVisibleCondition extends Condition {
        public FragmentNotVisibleCondition() {
            super(/* isRunOnUiThread= */ false);
        }

        @Override
        protected ConditionStatus checkWithSuppliers() throws Exception {
            ActivityT activity = mActivityElement.get();
            if (activity == null) return fulfilled("Fragment's owning activity is destroyed");
            List<Fragment> fragments = activity.getSupportFragmentManager().getFragments();
            for (Fragment fragment : fragments) {
                if (mFragmentClass.equals(fragment.getClass()) && fragment.isVisible()) {
                    return error("Fragment %s is still visible, although not expected.", fragment);
                }
            }
            return fulfilled("Fragment is destroyed");
        }

        @Override
        public String buildDescription() {
            return "Fragment does not exist or not visible: " + mFragmentClass.getSimpleName();
        }
    }
}