Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53c4a32a authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Android (Google) Code Review
Browse files

Merge "Correct record activity snapshot condition." into main

parents c57b841e 2bbc80f4
Loading
Loading
Loading
Loading
+74 −83
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Supplier;

/**
 * Provides window based implementation of {@link OnBackInvokedDispatcher}.
@@ -271,7 +272,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
     * Returns false if the legacy back behavior should be used.
     */
    public boolean isOnBackInvokedCallbackEnabled() {
        return Checker.isOnBackInvokedCallbackEnabled(mChecker.getContext());
        return isOnBackInvokedCallbackEnabled(mChecker.getContext());
    }

    /**
@@ -394,7 +395,18 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
     * {@link OnBackInvokedCallback}.
     */
    public static boolean isOnBackInvokedCallbackEnabled(@NonNull Context context) {
        return Checker.isOnBackInvokedCallbackEnabled(context);
        final Context originalContext = context;
        while ((context instanceof ContextWrapper) && !(context instanceof Activity)) {
            context = ((ContextWrapper) context).getBaseContext();
        }
        final ActivityInfo activityInfo = (context instanceof Activity)
                ? ((Activity) context).getActivityInfo() : null;
        final ApplicationInfo applicationInfo = context.getApplicationInfo();

        return WindowOnBackInvokedDispatcher
                .isOnBackInvokedCallbackEnabled(activityInfo, applicationInfo,
                        () -> originalContext.obtainStyledAttributes(
                                new int[] {android.R.attr.windowSwipeToDismiss}), true);
    }

    @Override
@@ -426,7 +438,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
         */
        public boolean checkApplicationCallbackRegistration(int priority,
                OnBackInvokedCallback callback) {
            if (!isOnBackInvokedCallbackEnabled(getContext())
            if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(getContext())
                    && !(callback instanceof CompatOnBackInvokedCallback)) {
                Log.w(TAG,
                        "OnBackInvokedCallback is not enabled for the application."
@@ -445,12 +457,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
        private Context getContext() {
            return mContext.get();
        }
    }

        private static boolean isOnBackInvokedCallbackEnabled(@Nullable Context context) {
    /**
     * @hide
     */
    public static boolean isOnBackInvokedCallbackEnabled(@Nullable ActivityInfo activityInfo,
            @NonNull ApplicationInfo applicationInfo,
            @NonNull Supplier<TypedArray> windowAttrSupplier, boolean recycleTypedArray) {
        // new back is enabled if the feature flag is enabled AND the app does not explicitly
        // request legacy back.
            boolean featureFlagEnabled = ENABLE_PREDICTIVE_BACK;
            if (!featureFlagEnabled) {
        if (!ENABLE_PREDICTIVE_BACK) {
            return false;
        }

@@ -458,56 +475,31 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
            return true;
        }

            // If the context is null, return false to use legacy back.
            if (context == null) {
                Log.w(TAG, "OnBackInvokedCallback is not enabled because context is null.");
                return false;
            }

            boolean requestsPredictiveBack = false;

            // Check if the context is from an activity.
            Context originalContext = context;
            while ((context instanceof ContextWrapper) && !(context instanceof Activity)) {
                context = ((ContextWrapper) context).getBaseContext();
            }

            boolean shouldCheckActivity = false;

            if (context instanceof Activity) {
                final Activity activity = (Activity) context;

                final ActivityInfo activityInfo = activity.getActivityInfo();
                if (activityInfo != null) {
                    if (activityInfo.hasOnBackInvokedCallbackEnabled()) {
                        shouldCheckActivity = true;
        boolean requestsPredictiveBack;
        // Activity
        if (activityInfo != null && activityInfo.hasOnBackInvokedCallbackEnabled()) {
            requestsPredictiveBack = activityInfo.isOnBackInvokedCallbackEnabled();

            if (DEBUG) {
                Log.d(TAG, TextUtils.formatSimple(
                        "Activity: %s isPredictiveBackEnabled=%s",
                                    activity.getComponentName(),
                        activityInfo.getComponentName(),
                        requestsPredictiveBack));
            }
                    }
                } else {
                    Log.w(TAG, "The ActivityInfo is null, so we cannot verify if this Activity"
                            + " has the 'android:enableOnBackInvokedCallback' attribute."
                            + " The application attribute will be used as a fallback.");
                }
            return requestsPredictiveBack;
        }

            if (!shouldCheckActivity) {
                final ApplicationInfo applicationInfo = context.getApplicationInfo();
        // Application
        requestsPredictiveBack = applicationInfo.isOnBackInvokedCallbackEnabled();

        if (DEBUG) {
            Log.d(TAG, TextUtils.formatSimple("App: %s requestsPredictiveBack=%s",
                    applicationInfo.packageName,
                    requestsPredictiveBack));
        }
        if (requestsPredictiveBack) {
            return true;
        }

                if (PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE && !requestsPredictiveBack) {
        if (PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE) {
            // Compatibility check for legacy window style flag used by Wear OS.
            // Note on compatibility behavior:
            // 1. windowSwipeToDismiss should be respected for all apps not opted in.
@@ -518,14 +510,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
            //    setTrigger(true)
            // Use the original context to resolve the styled attribute so that they stay
            // true to the window.
                    TypedArray windowAttr =
                            originalContext.obtainStyledAttributes(
                                    new int[] {android.R.attr.windowSwipeToDismiss});
            TypedArray windowAttr = windowAttrSupplier.get();
            boolean windowSwipeToDismiss = true;
            if (windowAttr != null) {
                if (windowAttr.getIndexCount() > 0) {
                    windowSwipeToDismiss = windowAttr.getBoolean(0, true);
                }
                if (recycleTypedArray) {
                    windowAttr.recycle();
                }
            }

            if (DEBUG) {
                Log.i(TAG, "falling back to windowSwipeToDismiss: " + windowSwipeToDismiss);
@@ -533,9 +527,6 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {

            requestsPredictiveBack = windowSwipeToDismiss;
        }
            }

        return requestsPredictiveBack;
    }
}
}
+7 −0
Original line number Diff line number Diff line
@@ -353,6 +353,7 @@ import android.window.SplashScreenView.SplashScreenViewParcelable;
import android.window.TaskSnapshot;
import android.window.TransitionInfo.AnimationOptions;
import android.window.WindowContainerToken;
import android.window.WindowOnBackInvokedDispatcher;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -978,6 +979,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    int mAllowedTouchUid;
    // Whether client has requested a scene transition when exiting.
    final boolean mHasSceneTransition;
    // Whether the app has opt-in enableOnBackInvokedCallback.
    final boolean mOptInOnBackInvoked;

    // Whether the ActivityEmbedding is enabled on the app.
    private final boolean mAppActivityEmbeddingSplitsEnabled;
@@ -2231,6 +2234,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            // No such property name.
        }
        mAppActivityEmbeddingSplitsEnabled = appActivityEmbeddingEnabled;

        mOptInOnBackInvoked = WindowOnBackInvokedDispatcher
                .isOnBackInvokedCallbackEnabled(info, info.applicationInfo,
                        () -> ent != null ? ent.array : null, false);
    }

    /**
+63 −7
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;

import android.os.Trace;
import android.util.ArrayMap;
import android.view.WindowManager;
import android.window.TaskSnapshot;

@@ -80,6 +81,7 @@ class SnapshotController {
        if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM) {
            return;
        }
        ActivitiesByTask activityTargets = null;
        for (int i = changeInfos.size() - 1; i >= 0; --i) {
            Transition.ChangeInfo info = changeInfos.get(i);
            // Intentionally skip record snapshot for changes originated from PiP.
@@ -98,14 +100,68 @@ class SnapshotController {
                final TaskFragment tf = info.mContainer.asTaskFragment();
                final ActivityRecord ar = tf != null ? tf.getTopMostActivity()
                        : info.mContainer.asActivityRecord();
                if (ar != null && !ar.isVisibleRequested() && ar.getTask().isVisibleRequested()) {
                    final WindowState mainWindow = ar.findMainWindow(false);
                    // Only capture activity snapshot if this app has adapted to back predict
                    if (mainWindow != null
                            && mainWindow.getOnBackInvokedCallbackInfo() != null
                            && mainWindow.getOnBackInvokedCallbackInfo().isSystemCallback()) {
                        mActivitySnapshotController.recordSnapshot(ar);
                if (ar != null && ar.getTask().isVisibleRequested()) {
                    if (activityTargets == null) {
                        activityTargets = new ActivitiesByTask();
                    }
                    activityTargets.put(ar);
                }
            }
        }
        if (activityTargets != null) {
            activityTargets.recordSnapshot(mActivitySnapshotController);
        }
    }

    private static class ActivitiesByTask {
        final ArrayMap<Task, OpenCloseActivities> mActivitiesMap = new ArrayMap<>();

        void put(ActivityRecord ar) {
            OpenCloseActivities activities = mActivitiesMap.get(ar.getTask());
            if (activities == null) {
                activities = new OpenCloseActivities();
                mActivitiesMap.put(ar.getTask(), activities);
            }
            activities.add(ar);
        }

        void recordSnapshot(ActivitySnapshotController controller) {
            for (int i = mActivitiesMap.size() - 1; i >= 0; i--) {
                final OpenCloseActivities pair = mActivitiesMap.valueAt(i);
                pair.recordSnapshot(controller);
            }
        }

        static class OpenCloseActivities {
            final ArrayList<ActivityRecord> mOpenActivities = new ArrayList<>();
            final ArrayList<ActivityRecord> mCloseActivities = new ArrayList<>();

            void add(ActivityRecord ar) {
                if (ar.isVisibleRequested()) {
                    mOpenActivities.add(ar);
                } else {
                    mCloseActivities.add(ar);
                }
            }

            boolean allOpensOptInOnBackInvoked() {
                if (mOpenActivities.isEmpty()) {
                    return false;
                }
                for (int i = mOpenActivities.size() - 1; i >= 0; --i) {
                    if (!mOpenActivities.get(i).mOptInOnBackInvoked) {
                        return false;
                    }
                }
                return true;
            }

            void recordSnapshot(ActivitySnapshotController controller) {
                if (!allOpensOptInOnBackInvoked() || mCloseActivities.isEmpty()) {
                    return;
                }
                for (int i = mCloseActivities.size() - 1; i >= 0; --i) {
                    controller.recordSnapshot(mCloseActivities.get(i));
                }
            }
        }