Merge "Prevent callback registration when back flag is disabled" into tm-dev
This commit is contained in:
@@ -1482,8 +1482,6 @@ public class Dialog implements DialogInterface, Window.Callback,
|
|||||||
/**
|
/**
|
||||||
* Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
|
* Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
|
||||||
* dialog is attached to.
|
* dialog is attached to.
|
||||||
*
|
|
||||||
* Returns null if the dialog is not attached to a window with a decor.
|
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
|
public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ import android.widget.FrameLayout;
|
|||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.window.CompatOnBackInvokedCallback;
|
||||||
import android.window.ImeOnBackInvokedDispatcher;
|
import android.window.ImeOnBackInvokedDispatcher;
|
||||||
import android.window.OnBackInvokedCallback;
|
import android.window.OnBackInvokedCallback;
|
||||||
import android.window.OnBackInvokedDispatcher;
|
import android.window.OnBackInvokedDispatcher;
|
||||||
@@ -350,7 +351,7 @@ public class InputMethodService extends AbstractInputMethodService {
|
|||||||
private RingBuffer<MotionEvent> mPendingEvents;
|
private RingBuffer<MotionEvent> mPendingEvents;
|
||||||
private ImeOnBackInvokedDispatcher mImeDispatcher;
|
private ImeOnBackInvokedDispatcher mImeDispatcher;
|
||||||
private Boolean mBackCallbackRegistered = false;
|
private Boolean mBackCallbackRegistered = false;
|
||||||
private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
|
private final CompatOnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
|
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ import android.view.contentcapture.MainContentCaptureSession;
|
|||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Scroller;
|
import android.widget.Scroller;
|
||||||
import android.window.ClientWindowFrames;
|
import android.window.ClientWindowFrames;
|
||||||
|
import android.window.CompatOnBackInvokedCallback;
|
||||||
import android.window.OnBackInvokedCallback;
|
import android.window.OnBackInvokedCallback;
|
||||||
import android.window.OnBackInvokedDispatcher;
|
import android.window.OnBackInvokedDispatcher;
|
||||||
import android.window.SurfaceSyncer;
|
import android.window.SurfaceSyncer;
|
||||||
@@ -339,13 +340,12 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
/**
|
/**
|
||||||
* The top level {@link OnBackInvokedDispatcher}.
|
* The top level {@link OnBackInvokedDispatcher}.
|
||||||
*/
|
*/
|
||||||
private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher =
|
private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
|
||||||
new WindowOnBackInvokedDispatcher();
|
|
||||||
/**
|
/**
|
||||||
* Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events
|
* Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events
|
||||||
* to view root for apps using legacy back behavior.
|
* to view root for apps using legacy back behavior.
|
||||||
*/
|
*/
|
||||||
private OnBackInvokedCallback mCompatOnBackInvokedCallback;
|
private CompatOnBackInvokedCallback mCompatOnBackInvokedCallback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for notifying about global configuration changes.
|
* Callback for notifying about global configuration changes.
|
||||||
@@ -959,6 +959,8 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
|
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
|
||||||
|
|
||||||
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
|
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
|
||||||
|
mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher(
|
||||||
|
context.getApplicationInfo().isOnBackInvokedCallbackEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void addFirstDrawHandler(Runnable callback) {
|
public static void addFirstDrawHandler(Runnable callback) {
|
||||||
@@ -10836,13 +10838,6 @@ public final class ViewRootImpl implements ViewParent,
|
|||||||
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback);
|
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unregisterCompatOnBackInvokedCallback() {
|
|
||||||
if (mCompatOnBackInvokedCallback != null) {
|
|
||||||
mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback);
|
|
||||||
mCompatOnBackInvokedCallback = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTouchableRegion(Region r) {
|
public void setTouchableRegion(Region r) {
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
|
|||||||
30
core/java/android/window/CompatOnBackInvokedCallback.java
Normal file
30
core/java/android/window/CompatOnBackInvokedCallback.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.window;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marker interface for {@link OnBackInvokedCallback} used for backward compatibility between the
|
||||||
|
* new system back and the old back event dispatching. Callbacks implementing this interface are
|
||||||
|
* allowed to be registered even if <code>enableOnbackInvoked</code> is set to false in the
|
||||||
|
* application manifest.
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public interface CompatOnBackInvokedCallback extends OnBackInvokedCallback{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onBackInvoked();
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
import android.window.WindowOnBackInvokedDispatcher.Checker;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -50,6 +51,11 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private OnBackInvokedDispatcher mActualDispatcher = null;
|
private OnBackInvokedDispatcher mActualDispatcher = null;
|
||||||
private ImeOnBackInvokedDispatcher mImeDispatcher;
|
private ImeOnBackInvokedDispatcher mImeDispatcher;
|
||||||
|
private final Checker mChecker;
|
||||||
|
|
||||||
|
public ProxyOnBackInvokedDispatcher(boolean applicationCallBackEnabled) {
|
||||||
|
mChecker = new Checker(applicationCallBackEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerOnBackInvokedCallback(
|
public void registerOnBackInvokedCallback(
|
||||||
@@ -58,11 +64,9 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
Log.v(TAG, String.format("Proxy register %s. mActualDispatcher=%s", callback,
|
Log.v(TAG, String.format("Proxy register %s. mActualDispatcher=%s", callback,
|
||||||
mActualDispatcher));
|
mActualDispatcher));
|
||||||
}
|
}
|
||||||
if (priority < 0) {
|
if (mChecker.checkApplicationCallbackRegistration(priority, callback)) {
|
||||||
throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
|
registerOnBackInvokedCallbackUnchecked(callback, priority);
|
||||||
+ "cannot have negative priority. Priority: " + priority);
|
|
||||||
}
|
}
|
||||||
registerOnBackInvokedCallbackUnchecked(callback, priority);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import android.view.IWindowSession;
|
|||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,6 +63,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
/** Holds all callbacks by priorities. */
|
/** Holds all callbacks by priorities. */
|
||||||
private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
|
private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>>
|
||||||
mOnBackInvokedCallbacks = new TreeMap<>();
|
mOnBackInvokedCallbacks = new TreeMap<>();
|
||||||
|
private final Checker mChecker;
|
||||||
|
|
||||||
|
public WindowOnBackInvokedDispatcher(boolean applicationCallBackEnabled) {
|
||||||
|
mChecker = new Checker(applicationCallBackEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the pending top callback (if one exists) to WM when the view root
|
* Sends the pending top callback (if one exists) to WM when the view root
|
||||||
@@ -86,14 +92,16 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
@Override
|
@Override
|
||||||
public void registerOnBackInvokedCallback(
|
public void registerOnBackInvokedCallback(
|
||||||
@Priority int priority, @NonNull OnBackInvokedCallback callback) {
|
@Priority int priority, @NonNull OnBackInvokedCallback callback) {
|
||||||
if (priority < 0) {
|
if (mChecker.checkApplicationCallbackRegistration(priority, callback)) {
|
||||||
throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
|
registerOnBackInvokedCallbackUnchecked(callback, priority);
|
||||||
+ "cannot have negative priority. Priority: " + priority);
|
|
||||||
}
|
}
|
||||||
registerOnBackInvokedCallbackUnchecked(callback, priority);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerOnBackInvokedCallbackUnchecked(
|
/**
|
||||||
|
* Register a callback bypassing platform checks. This is used to register compatibility
|
||||||
|
* callbacks.
|
||||||
|
*/
|
||||||
|
public void registerOnBackInvokedCallbackUnchecked(
|
||||||
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
|
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
|
||||||
if (mImeDispatcher != null) {
|
if (mImeDispatcher != null) {
|
||||||
mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
|
mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
|
||||||
@@ -203,6 +211,14 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the checker used to check whether a callback can be registered
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Checker getChecker() {
|
||||||
|
return mChecker;
|
||||||
|
}
|
||||||
|
|
||||||
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
|
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
|
||||||
private final WeakReference<OnBackInvokedCallback> mCallback;
|
private final WeakReference<OnBackInvokedCallback> mCallback;
|
||||||
|
|
||||||
@@ -289,4 +305,41 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
|
|||||||
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
|
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
|
||||||
mImeDispatcher = imeDispatcher;
|
mImeDispatcher = imeDispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to check whether a callback can be registered or not. This is meant to be
|
||||||
|
* shared with {@link ProxyOnBackInvokedDispatcher} which needs to do the same checks.
|
||||||
|
*/
|
||||||
|
public static class Checker {
|
||||||
|
|
||||||
|
private final boolean mApplicationCallBackEnabled;
|
||||||
|
|
||||||
|
public Checker(boolean applicationCallBackEnabled) {
|
||||||
|
mApplicationCallBackEnabled = applicationCallBackEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given callback can be registered with the given priority.
|
||||||
|
* @return true if the callback can be added.
|
||||||
|
* @throws IllegalArgumentException if the priority is negative.
|
||||||
|
*/
|
||||||
|
public boolean checkApplicationCallbackRegistration(int priority,
|
||||||
|
OnBackInvokedCallback callback) {
|
||||||
|
if (!mApplicationCallBackEnabled
|
||||||
|
&& !(callback instanceof CompatOnBackInvokedCallback)) {
|
||||||
|
Log.w("OnBackInvokedCallback",
|
||||||
|
"OnBackInvokedCallback is not enabled for the application."
|
||||||
|
+ "\nSet 'android:enableOnBackInvokedCallback=\"true\"' in the"
|
||||||
|
+ " application manifest.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (priority < 0) {
|
||||||
|
throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
|
||||||
|
+ "cannot have negative priority. Priority: " + priority);
|
||||||
|
}
|
||||||
|
Objects.requireNonNull(callback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,8 +342,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
|||||||
|
|
||||||
boolean mDecorFitsSystemWindows = true;
|
boolean mDecorFitsSystemWindows = true;
|
||||||
|
|
||||||
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher =
|
private final ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher;
|
||||||
new ProxyOnBackInvokedDispatcher();
|
|
||||||
|
|
||||||
static class WindowManagerHolder {
|
static class WindowManagerHolder {
|
||||||
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
|
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
|
||||||
@@ -358,6 +357,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
|
|||||||
mLayoutInflater = LayoutInflater.from(context);
|
mLayoutInflater = LayoutInflater.from(context);
|
||||||
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
|
mRenderShadowsInCompositor = Settings.Global.getInt(context.getContentResolver(),
|
||||||
DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
|
DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
|
||||||
|
mProxyOnBackInvokedDispatcher = new ProxyOnBackInvokedDispatcher(
|
||||||
|
context.getApplicationInfo().isOnBackInvokedCallbackEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -161,7 +161,10 @@
|
|||||||
<!-- ChooserActivityTest permissions-->
|
<!-- ChooserActivityTest permissions-->
|
||||||
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
|
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
|
||||||
|
|
||||||
<application android:theme="@style/Theme" android:supportsRtl="true">
|
<application
|
||||||
|
android:theme="@style/Theme"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:enableOnBackInvokedCallback="true">
|
||||||
<uses-library android:name="android.test.runner" />
|
<uses-library android:name="android.test.runner" />
|
||||||
<uses-library android:name="org.apache.http.legacy" android:required="false" />
|
<uses-library android:name="org.apache.http.legacy" android:required="false" />
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ public class WindowOnBackInvokedDispatcherTest {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mDispatcher = new WindowOnBackInvokedDispatcher();
|
mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */);
|
||||||
mDispatcher.attachToWindow(mWindowSession, mWindow);
|
mDispatcher.attachToWindow(mWindowSession, mWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
|||||||
|
|
||||||
Task task = createTopTaskWithActivity();
|
Task task = createTopTaskWithActivity();
|
||||||
WindowState appWindow = task.getTopVisibleAppMainWindow();
|
WindowState appWindow = task.getTopVisibleAppMainWindow();
|
||||||
WindowOnBackInvokedDispatcher dispatcher = new WindowOnBackInvokedDispatcher();
|
WindowOnBackInvokedDispatcher dispatcher =
|
||||||
|
new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */);
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1));
|
appWindow.setOnBackInvokedCallbackInfo(invocation.getArgument(1));
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
Reference in New Issue
Block a user