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

Commit 224c1343 authored by Johannes Gallmann's avatar Johannes Gallmann
Browse files

Remove restriction that only one callback with...

...PRIORITY_SYSTEM_NAVIGATION_OBSERVER can be registered.

This CL removes the API restriction that only one back callback with
PRIORITY_SYSTEM_NAVIGATION_OBSERVER can be registered at a time.
Starting with SDK 37, an unrestricted number of observer callbacks can
be registered. All registered observer callbacks will receive back
events when a system back navigation happens.

Bug: 424146227
Test: WindowOnBackInvokedDispatcherTest
Flag: com.android.window.flags.multiple_system_navigation_observer_callbacks
Change-Id: Ibed2eb513e7a3c80cab128f4f884d359a06c01f4
parent ee7cfd43
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -7734,7 +7734,7 @@ public final class ViewRootImpl implements ViewParent,
                        if (keyEvent.isCanceled()) {
                            animationCallback.onBackCancelled();
                        } else {
                            dispatcher.tryInvokeSystemNavigationObserverCallback();
                            dispatcher.tryInvokeSystemNavigationObserverCallbacks();
                            topCallback.onBackInvoked();
                        }
                        break;
@@ -7742,7 +7742,7 @@ public final class ViewRootImpl implements ViewParent,
            } else if (topCallback != null) {
                if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
                    if (!keyEvent.isCanceled()) {
                        dispatcher.tryInvokeSystemNavigationObserverCallback();
                        dispatcher.tryInvokeSystemNavigationObserverCallbacks();
                        topCallback.onBackInvoked();
                    } else {
                        Log.d(mTag, "Skip onBackInvoked(), reason: keyEvent.isCanceled=true");
+15 −5
Original line number Diff line number Diff line
@@ -74,12 +74,22 @@ public interface OnBackInvokedDispatcher {
     * Priority level of {@link OnBackInvokedCallback}s designed to observe system-level back
     * handling.
     *
     * <p>Callbacks registered with this priority do not consume back events. They receive back
     * events whenever the system handles a back navigation and have no impact on the normal back
     * navigation flow. Useful for logging or analytics.
     * <p>Callbacks registered with this priority are invoked only when a back event is not
     * consumed by any other registered {@link OnBackInvokedCallback} with a priority of
     * {@link #PRIORITY_DEFAULT} or higher. This means they are called when the system is about
     * to perform its default back navigation action, such as finishing the current activity or
     * executing a back-to-home animation.
     *
     * <p>Only one callback with {@link #PRIORITY_SYSTEM_NAVIGATION_OBSERVER} can be registered at a
     * time.
     * <p>These callbacks are purely observational. They do not consume the back event and
     * cannot intercept it. Their invocation has no impact on the normal back navigation flow,
     * making them suitable for logging, analytics, or triggering actions that should not
     * prevent the system's own back handling.
     *
     * <p>The invocation order for multiple callbacks registered with this priority is undefined.
     *
     * <p>On API level 36 only, there is a restriction that only one callback with
     * {@link #PRIORITY_SYSTEM_NAVIGATION_OBSERVER} can be registered at a time. On API level 37 and
     * higher, the number of registered observer callbacks is unrestricted.
     */
    @FlaggedApi(Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
    int PRIORITY_SYSTEM_NAVIGATION_OBSERVER = -2;
+51 −14
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.window;

import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;

import static com.android.window.flags.Flags.multipleSystemNavigationObserverCallbacks;
import static com.android.window.flags.Flags.predictiveBackSystemOverrideCallback;
import static com.android.window.flags.Flags.predictiveBackPrioritySystemNavigationObserver;
import static com.android.window.flags.Flags.predictiveBackTimestampApi;
@@ -54,7 +55,9 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BooleanSupplier;
import java.util.function.Supplier;
@@ -111,6 +114,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {

    @VisibleForTesting
    public OnBackInvokedCallback mSystemNavigationObserverCallback = null;
    @VisibleForTesting
    public Set<OnBackInvokedCallback> mSystemNavigationObserverCallbacks = new HashSet<>();

    private Checker mChecker;
    private final Object mLock = new Object();
@@ -189,9 +194,13 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
                }
                removeCallbackInternal(callback);
            }
            if (multipleSystemNavigationObserverCallbacks()) {
                mSystemNavigationObserverCallbacks.add(callback);
            } else {
                mSystemNavigationObserverCallback = callback;
            }
        }
    }

    /**
     * Register a callback bypassing platform checks. This is used to register compatibility
@@ -240,6 +249,15 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
                Integer prevPriority = mAllCallbacks.get(callback);
                mOnBackInvokedCallbacks.get(prevPriority).remove(callback);
            }
            if (multipleSystemNavigationObserverCallbacks()) {
                if (mSystemNavigationObserverCallbacks.contains(callback)) {
                    mSystemNavigationObserverCallbacks.remove(callback);
                    if (DEBUG) {
                        Log.i(TAG, "Callback already registered (as system-navigation-observer "
                                + "callback). Removing and re-adding it.");
                    }
                }
            } else {
                if (mSystemNavigationObserverCallback == callback) {
                    mSystemNavigationObserverCallback = null;
                    if (DEBUG) {
@@ -247,6 +265,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
                                + "callback). Removing and re-adding it.");
                    }
                }
            }

            OnBackInvokedCallback previousTopCallback = getTopCallback();
            callbacks.add(callback);
@@ -266,10 +285,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
                mImeDispatcher.unregisterOnBackInvokedCallback(callback);
                return;
            }
            if (multipleSystemNavigationObserverCallbacks()) {
                if (mSystemNavigationObserverCallbacks.contains(callback)) {
                    mSystemNavigationObserverCallbacks.remove(callback);
                    return;
                }
            } else {
                if (mSystemNavigationObserverCallback == callback) {
                    mSystemNavigationObserverCallback = null;
                    return;
                }
            }
            if (callback instanceof ImeOnBackInvokedDispatcher.DefaultImeOnBackAnimationCallback) {
                callback = mImeBackAnimationController;
            }
@@ -373,6 +399,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
            mAllCallbacks.clear();
            mOnBackInvokedCallbacks.clear();
            mSystemNavigationObserverCallback = null;
            mSystemNavigationObserverCallbacks.clear();
        }
    }

@@ -385,11 +412,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
    }

    /**
     * Tries to call {@link OnBackInvokedCallback#onBackInvoked} on the system navigation observer
     * callback (if one is set and if the top-most regular callback has
     * Tries to call {@link OnBackInvokedCallback#onBackInvoked} on all system navigation observer
     * callback (if any are registered and if the top-most regular callback has
     * {@link OnBackInvokedDispatcher#PRIORITY_SYSTEM})
     */
    public void tryInvokeSystemNavigationObserverCallback() {
    public void tryInvokeSystemNavigationObserverCallbacks() {
        OnBackInvokedCallback topCallback = getTopCallback();
        Integer callbackPriority = mAllCallbacks.getOrDefault(topCallback, null);
        final boolean isSystemOverride = topCallback instanceof SystemOverrideOnBackInvokedCallback;
@@ -399,10 +426,20 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
    }

    private void invokeSystemNavigationObserverCallback() {
        if (multipleSystemNavigationObserverCallbacks()) {
            Set<OnBackInvokedCallback> observerCallbacks;
            synchronized (mLock) {
                observerCallbacks = new HashSet<>(mSystemNavigationObserverCallbacks);
            }
            for (OnBackInvokedCallback callback : observerCallbacks) {
                callback.onBackInvoked();
            }
        } else {
            if (mSystemNavigationObserverCallback != null) {
                mSystemNavigationObserverCallback.onBackInvoked();
            }
        }
    }

    private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
        if (mWindowSession == null || mWindow == null) {
+7 −0
Original line number Diff line number Diff line
@@ -522,3 +522,10 @@ flag {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "multiple_system_navigation_observer_callbacks"
    namespace: "windowing_frontend"
    description: "Removes restriction that only one back callback with PRIORITY_SYSTEM_NAVIGATION_OBSERVER can be registered"
    bug: "424146227"
}
+37 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.window.OnBackInvokedDispatcher.PRIORITY_DEFAULT;
import static android.window.OnBackInvokedDispatcher.PRIORITY_OVERLAY;
import static android.window.OnBackInvokedDispatcher.PRIORITY_SYSTEM_NAVIGATION_OBSERVER;

import static com.android.window.flags.Flags.FLAG_MULTIPLE_SYSTEM_NAVIGATION_OBSERVER_CALLBACKS;
import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER;
import static com.android.window.flags.Flags.FLAG_PREDICTIVE_BACK_TIMESTAMP_API;

@@ -167,7 +168,8 @@ public class WindowOnBackInvokedDispatcherTest {
        int actualSizeOverlay = callbacksOverlay != null ? callbacksOverlay.size() : 0;
        assertEquals("mOnBackInvokedCallbacks OVERLAY size", expectedOverlay, actualSizeOverlay);

        int actualSizeObserver = mDispatcher.mSystemNavigationObserverCallback == null ? 0 : 1;
        int actualSizeObserver = mDispatcher.mSystemNavigationObserverCallback == null
                ? mDispatcher.mSystemNavigationObserverCallbacks.size() : 1;
        assertEquals("mOnBackInvokedCallbacks SYSTEM_NAVIGATION_OBSERVER size", expectedObserver,
                actualSizeObserver);
    }
@@ -666,6 +668,7 @@ public class WindowOnBackInvokedDispatcherTest {

    @Test
    @RequiresFlagsEnabled(FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER)
    @RequiresFlagsDisabled(FLAG_MULTIPLE_SYSTEM_NAVIGATION_OBSERVER_CALLBACKS)
    public void testObserverCallback_reregistrations() {
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
@@ -688,6 +691,39 @@ public class WindowOnBackInvokedDispatcherTest {
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 0);
    }

    @Test
    @RequiresFlagsEnabled({FLAG_PREDICTIVE_BACK_PRIORITY_SYSTEM_NAVIGATION_OBSERVER,
            FLAG_MULTIPLE_SYSTEM_NAVIGATION_OBSERVER_CALLBACKS})
    public void testObserverCallback_multiple_registrations() {
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);
        assertTrue(mDispatcher.mSystemNavigationObserverCallbacks.contains(mCallback1));

        // test registration of another observer-callback
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback2);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 2);
        assertTrue(mDispatcher.mSystemNavigationObserverCallbacks.contains(mCallback1));
        assertTrue(mDispatcher.mSystemNavigationObserverCallbacks.contains(mCallback2));

        // test reregistration of first observer-callback
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 2);

        // test reregistration of observer-callback with default priority
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_DEFAULT, mCallback1);
        assertCallbacksSize(/* default */ 1, /* overlay */ 0, /* observer */ 1);

        // test reregistration of regular callback as observer-callback
        mDispatcher.registerOnBackInvokedCallback(PRIORITY_SYSTEM_NAVIGATION_OBSERVER, mCallback1);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 2);

        mDispatcher.unregisterOnBackInvokedCallback(mCallback2);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 1);

        mDispatcher.unregisterOnBackInvokedCallback(mCallback1);
        assertCallbacksSize(/* default */ 0, /* overlay */ 0, /* observer */ 0);
    }

    private BackMotionEvent backMotionEventFrom(float progress) {
        return new BackMotionEvent(
                /* touchX = */ 0,