diff --git a/core/api/current.txt b/core/api/current.txt index 8a689689cdc29..3dcda31fdf0e5 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -3995,7 +3995,7 @@ package android.app { method @Deprecated public void onTabUnselected(android.app.ActionBar.Tab, android.app.FragmentTransaction); } - @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback { + @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback { ctor public Activity(); method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public void closeContextMenu(); @@ -4038,6 +4038,7 @@ package android.app { method public int getMaxNumPictureInPictureActions(); method public final android.media.session.MediaController getMediaController(); method @NonNull public android.view.MenuInflater getMenuInflater(); + method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method public final android.app.Activity getParent(); method @Nullable public android.content.Intent getParentActivityIntent(); method public android.content.SharedPreferences getPreferences(int); @@ -4916,7 +4917,7 @@ package android.app { method public void onDateSet(android.widget.DatePicker, int, int, int); } - public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.View.OnCreateContextMenuListener android.view.Window.Callback { + public class Dialog implements android.content.DialogInterface android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback { ctor public Dialog(@NonNull @UiContext android.content.Context); ctor public Dialog(@NonNull @UiContext android.content.Context, @StyleRes int); ctor protected Dialog(@NonNull @UiContext android.content.Context, boolean, @Nullable android.content.DialogInterface.OnCancelListener); @@ -4936,6 +4937,7 @@ package android.app { method @NonNull @UiContext public final android.content.Context getContext(); method @Nullable public android.view.View getCurrentFocus(); method @NonNull public android.view.LayoutInflater getLayoutInflater(); + method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method @Nullable public final android.app.Activity getOwnerActivity(); method @Nullable public final android.view.SearchEvent getSearchEvent(); method public final int getVolumeControlStream(); @@ -49478,6 +49480,22 @@ package android.view { field public int toolType; } + public interface OnBackInvokedCallback { + method public default void onBackInvoked(); + } + + public abstract class OnBackInvokedDispatcher { + ctor public OnBackInvokedDispatcher(); + method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int); + method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback); + field public static final int PRIORITY_DEFAULT = 0; // 0x0 + field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240 + } + + public interface OnBackInvokedDispatcherOwner { + method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); + } + public interface OnReceiveContentListener { method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo); } @@ -49901,7 +49919,7 @@ package android.view { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { ctor public View(android.content.Context); ctor public View(android.content.Context, @Nullable android.util.AttributeSet); ctor public View(android.content.Context, @Nullable android.util.AttributeSet, int); @@ -50105,6 +50123,7 @@ package android.view { method @IdRes public int getNextFocusLeftId(); method @IdRes public int getNextFocusRightId(); method @IdRes public int getNextFocusUpId(); + method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); method @ColorInt public int getOutlineAmbientShadowColor(); method public android.view.ViewOutlineProvider getOutlineProvider(); diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index de8fe8f2a6e93..2be0c5b1e36bc 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -9,7 +9,7 @@ package android { package android.app { - @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback { + @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback { method public final boolean addDumpable(@NonNull android.util.Dumpable); } diff --git a/core/api/removed.txt b/core/api/removed.txt index 07639fbf5378b..311b110f19976 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -513,7 +513,7 @@ package android.util { package android.view { - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { method protected void initializeFadingEdge(android.content.res.TypedArray); method protected void initializeScrollbars(android.content.res.TypedArray); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index d1178e39d930a..87c62f488fcfa 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -444,7 +444,7 @@ package android.accounts { package android.app { - @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback { + @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback { method public void convertFromTranslucent(); method public boolean convertToTranslucent(android.app.Activity.TranslucentConversionListener, android.app.ActivityOptions); method @Deprecated public boolean isBackgroundVisibleBehind(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d6a9f7faea657..0e52c5df06d77 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -101,7 +101,7 @@ package android.animation { package android.app { - @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback { + @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.OnBackInvokedDispatcherOwner android.view.View.OnCreateContextMenuListener android.view.Window.Callback { method public void onMovedToDisplay(int, android.content.res.Configuration); } @@ -2779,7 +2779,7 @@ package android.view { method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams); } - @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback { + @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner { method public android.view.View getTooltipView(); method public boolean isAutofilled(); method public static boolean isDefaultFocusHighlightEnabled(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 283345f073370..1e0b143749f55 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -111,6 +111,8 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; import android.view.RemoteAnimationDefinition; import android.view.SearchEvent; import android.view.View; @@ -736,7 +738,8 @@ public class Activity extends ContextThemeWrapper Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, - ContentCaptureManager.ContentCaptureClient { + ContentCaptureManager.ContentCaptureClient, + OnBackInvokedDispatcherOwner { private static final String TAG = "Activity"; private static final boolean DEBUG_LIFECYCLE = false; @@ -8672,4 +8675,22 @@ public class Activity extends ContextThemeWrapper return (w != null && w.peekDecorView() != null); } } + + /** + * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this + * activity is attached to. + * + * Returns null if the activity is not attached to a window with a decor. + */ + @Nullable + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + if (mWindow != null) { + View decorView = mWindow.getDecorView(); + if (decorView != null) { + return decorView.getOnBackInvokedDispatcher(); + } + } + return null; + } } diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 306035341ea3a..a7fb83bfcf5ef 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -52,6 +52,8 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; +import android.view.OnBackInvokedDispatcherOwner; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; @@ -94,7 +96,8 @@ import java.lang.ref.WeakReference; * */ public class Dialog implements DialogInterface, Window.Callback, - KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback { + KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback, + OnBackInvokedDispatcherOwner { private static final String TAG = "Dialog"; @UnsupportedAppUsage private Activity mOwnerActivity; @@ -1439,4 +1442,22 @@ public class Dialog implements DialogInterface, Window.Callback, } } } + + /** + * Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this + * dialog is attached to. + * + * Returns null if the dialog is not attached to a window with a decor. + */ + @Nullable + @Override + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + if (mWindow != null) { + View decorView = mWindow.getDecorView(); + if (decorView != null) { + return decorView.getOnBackInvokedDispatcher(); + } + } + return null; + } } diff --git a/core/java/android/view/OnBackInvokedCallback.java b/core/java/android/view/OnBackInvokedCallback.java new file mode 100644 index 0000000000000..b5cd89cfa4e13 --- /dev/null +++ b/core/java/android/view/OnBackInvokedCallback.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.app.Activity; +import android.app.Dialog; + +/** + * Interface for applications to register back invocation callbacks. This allows the client + * to customize various back behaviors by overriding the corresponding callback methods. + * + * Callback instances can be added to and removed from {@link OnBackInvokedDispatcher}, held + * by classes that implement {@link OnBackInvokedDispatcherOwner} (such as {@link Activity}, + * {@link Dialog} and {@link View}). + * + * Under the hood callbacks are registered at window level. When back is triggered, + * callbacks on the in-focus window are invoked in reverse order in which they are added + * within the same priority. Between different pirorities, callbacks with higher priority + * are invoked first. + * + * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)} + * for specifying callback priority. + */ +public interface OnBackInvokedCallback { + /** + * Called when a back gesture has been started, or back button has been pressed down. + * + * @hide + */ + default void onBackStarted() { }; + + /** + * Called on back gesture progress. + * + * @param touchX Absolute X location of the touch point. + * @param touchY Absolute Y location of the touch point. + * @param progress Value between 0 and 1 on how far along the back gesture is. + * + * @hide + */ + // TODO(b/210539672): combine back progress params into BackEvent. + default void onBackProgressed(int touchX, int touchY, float progress) { }; + + /** + * Called when a back gesture or back button press has been cancelled. + * + * @hide + */ + default void onBackCancelled() { }; + + /** + * Called when a back gesture has been completed and committed, or back button pressed + * has been released and committed. + */ + default void onBackInvoked() { }; +} diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java new file mode 100644 index 0000000000000..05c312b56cc72 --- /dev/null +++ b/core/java/android/view/OnBackInvokedDispatcher.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SuppressLint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Dispatcher to register {@link OnBackInvokedCallback} instances for handling + * back invocations. + * + * It also provides interfaces to update the attributes of {@link OnBackInvokedCallback}. + * Attribute updates are proactively pushed to the window manager if they change the dispatch + * target (a.k.a. the callback to be invoked next), or its behavior. + */ +public abstract class OnBackInvokedDispatcher { + /** @hide */ + @IntDef({ + PRIORITY_DEFAULT, + PRIORITY_OVERLAY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Priority{} + + /** + * Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and + * navigation drawers that should receive back dispatch before non-overlays. + */ + public static final int PRIORITY_OVERLAY = 1000000; + + /** + * Default priority level of {@link OnBackInvokedCallback}s. + */ + public static final int PRIORITY_DEFAULT = 0; + + /** + * Registers a {@link OnBackInvokedCallback}. + * + * Within the same priority level, callbacks are invoked in the reverse order in which + * they are registered. Higher priority callbacks are invoked before lower priority ones. + * + * @param callback The callback to be registered. If the callback instance has been already + * registered, the existing instance (no matter its priority) will be + * unregistered and registered again. + * @param priority The priority of the callback. + */ + @SuppressLint("SamShouldBeLast") + public abstract void registerOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback, @Priority int priority); + + /** + * Unregisters a {@link OnBackInvokedCallback}. + * + * @param callback The callback to be unregistered. Does nothing if the callback has not been + * registered. + */ + public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback); +} diff --git a/core/java/android/view/OnBackInvokedDispatcherOwner.java b/core/java/android/view/OnBackInvokedDispatcherOwner.java new file mode 100644 index 0000000000000..0e14ed4cdb07b --- /dev/null +++ b/core/java/android/view/OnBackInvokedDispatcherOwner.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.Nullable; + +/** + * A class that provides an {@link OnBackInvokedDispatcher} that allows you to register + * an {@link OnBackInvokedCallback} for handling the system back invocation behavior. + */ +public interface OnBackInvokedDispatcherOwner { + /** + * Returns the {@link OnBackInvokedDispatcher} that should dispatch the back invocation + * to its registered {@link OnBackInvokedCallback}s. + * Returns null when the root view is not attached to a window or a view tree with a decor. + */ + @Nullable + OnBackInvokedDispatcher getOnBackInvokedDispatcher(); +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 41a31e1011f24..2126fd523c45b 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -834,7 +834,7 @@ import java.util.function.Predicate; */ @UiThread public class View implements Drawable.Callback, KeyEvent.Callback, - AccessibilityEventSource { + AccessibilityEventSource, OnBackInvokedDispatcherOwner { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private static final boolean DBG = false; @@ -31276,4 +31276,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } return null; } + + /** + * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to. + * + * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached + * to a window or a view tree with a decor. + */ + @Nullable + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + ViewParent parent = getParent(); + if (parent instanceof View) { + return ((View) parent).getOnBackInvokedDispatcher(); + } else if (parent instanceof ViewRootImpl) { + // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have + // a {@link com.android.internal.policy.DecorView}. + return ((ViewRootImpl) parent).getOnBackInvokedDispatcher(); + } + return null; + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 74129312b41b3..cec8d4c89c5e0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -192,6 +192,7 @@ import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import android.window.ClientWindowFrames; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -308,6 +309,12 @@ public final class ViewRootImpl implements ViewParent, new ArrayList<>(); private @SurfaceControl.BufferTransform int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY; + /** + * The fallback {@link OnBackInvokedDispatcher} when the window doesn't have a decor view. + */ + private WindowOnBackInvokedDispatcher mFallbackOnBackInvokedDispatcher = + new WindowOnBackInvokedDispatcher(); + /** * Callback for notifying about global configuration changes. */ @@ -871,6 +878,7 @@ public final class ViewRootImpl implements ViewParent, mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled(); mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS; + mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow); } public static void addFirstDrawHandler(Runnable callback) { @@ -1120,6 +1128,9 @@ public final class ViewRootImpl implements ViewParent, if (pendingInsetsController != null) { pendingInsetsController.replayAndAttach(mInsetsController); } + ((RootViewSurfaceTaker) mView) + .provideWindowOnBackInvokedDispatcher() + .attachToWindow(mWindowSession, mWindow); } try { @@ -10634,7 +10645,7 @@ public final class ViewRootImpl implements ViewParent, } mBLASTDrawConsumer = consume; return true; - } + } boolean wasRelayoutRequested() { return mRelayoutRequested; @@ -10644,4 +10655,16 @@ public final class ViewRootImpl implements ViewParent, mForceNextWindowRelayout = true; scheduleTraversals(); } + + /** + * Returns the {@link OnBackInvokedDispatcher} on the decor view if one exists, or the + * fallback {@link OnBackInvokedDispatcher} instance. + */ + @Nullable + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + if (mView instanceof RootViewSurfaceTaker) { + return ((RootViewSurfaceTaker) mView).provideWindowOnBackInvokedDispatcher(); + } + return mFallbackOnBackInvokedDispatcher; + } } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java new file mode 100644 index 0000000000000..53734eb887204 --- /dev/null +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.OnBackInvokedCallback; +import android.view.OnBackInvokedDispatcher; + +/** + * Provides window based implementation of {@link OnBackInvokedDispatcher}. + * + * Callbacks with higher priorities receive back dispatching first. + * Within the same priority, callbacks receive back dispatching in the reverse order + * in which they are added. + * + * When the top priority callback is updated, the new callback is propagated to the Window Manager + * if the window the instance is associated with has been attached. It is allowed to register / + * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks + * will not receive dispatches until window attachment. + * + * @hide + */ +public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher { + private IWindowSession mWindowSession; + private IWindow mWindow; + + /** + * Sends the pending top callback (if one exists) to WM when the view root + * is attached a window. + */ + public void attachToWindow(@NonNull IWindowSession windowSession, @NonNull IWindow window) { + mWindowSession = windowSession; + mWindow = window; + // TODO(b/209867448): Send the top callback to WM (if one exists). + } + + /** Detaches the dispatcher instance from its window. */ + public void detachFromWindow() { + mWindow = null; + mWindowSession = null; + } + + @Override + public void registerOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback, @Priority int priority) { + // TODO(b/209867448): To be implemented. + } + + @Override + public void unregisterOnBackInvokedCallback( + @NonNull OnBackInvokedCallback callback) { + // TODO(b/209867448): To be implemented. + } + + /** Clears all registered callbacks on the instance. */ + public void clear() { + // TODO(b/209867448): To be implemented. + } +} diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index ba7a0ef893d0f..d7eeb7b8dec0d 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -87,6 +87,7 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.OnBackInvokedDispatcher; import android.view.PendingInsetsController; import android.view.ThreadedRenderer; import android.view.View; @@ -108,6 +109,7 @@ import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.PopupWindow; +import android.window.WindowOnBackInvokedDispatcher; import com.android.internal.R; import com.android.internal.graphics.drawable.BackgroundBlurDrawable; @@ -294,6 +296,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return true; }; private Consumer mCrossWindowBlurEnabledListener; + private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher; DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) { @@ -322,6 +325,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initResizingPaints(); mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); + mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(); } void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { @@ -1869,6 +1873,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } mPendingInsetsController.detach(); + mOnBackInvokedDispatcher.detachFromWindow(); } @Override @@ -1913,6 +1918,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind return mPendingInsetsController; } + @Override + public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() { + return mOnBackInvokedDispatcher; + } + private ActionMode createActionMode( int type, ActionMode.Callback2 callback, View originatingView) { switch (type) { @@ -2367,6 +2377,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } } + mOnBackInvokedDispatcher.clear(); } @Override @@ -2648,6 +2659,15 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } + /** + * Returns the {@link OnBackInvokedDispatcher} on the decor view. + */ + @Override + @Nullable + public OnBackInvokedDispatcher getOnBackInvokedDispatcher() { + return mOnBackInvokedDispatcher; + } + @Override public String toString() { return "DecorView@" + Integer.toHexString(this.hashCode()) + "[" diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index efd5fb2e1edf9..4b89bf5082ba6 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -15,11 +15,12 @@ */ package com.android.internal.view; +import android.annotation.NonNull; import android.annotation.Nullable; import android.view.InputQueue; import android.view.PendingInsetsController; import android.view.SurfaceHolder; -import android.view.WindowInsetsController; +import android.window.WindowOnBackInvokedDispatcher; /** hahahah */ public interface RootViewSurfaceTaker { @@ -30,4 +31,6 @@ public interface RootViewSurfaceTaker { InputQueue.Callback willYouTakeTheInputQueue(); void onRootViewScrollYChanged(int scrollY); @Nullable PendingInsetsController providePendingInsetsController(); + /** @hide */ + @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher(); }