Merge "Migrate IME to handle back with OnBackInvokedDispatcher." into tm-dev am: e3a61826eb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17952046

Change-Id: I8b92457b1d40be20e666f153249d6c72da7bc684
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Shan Huang
2022-05-06 19:52:44 +00:00
committed by Automerger Merge Worker
17 changed files with 461 additions and 52 deletions

View File

@@ -800,6 +800,7 @@ package android.content.pm {
method public boolean hasRequestForegroundServiceExemption();
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
method public void setEnableOnBackInvokedCallback(boolean);
field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8
field public int privateFlags;
}

View File

@@ -2752,4 +2752,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US));
}
}
/**
* Sets whether the application will use the {@link android.window.OnBackInvokedCallback}
* navigation system instead of the {@link android.view.KeyEvent#KEYCODE_BACK} and related
* callbacks. Intended to be used from tests only.
*
* @see #isOnBackInvokedCallbackEnabled()
* @hide
*/
@TestApi
public void setEnableOnBackInvokedCallback(boolean isEnable) {
if (isEnable) {
privateFlagsExt |= PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
} else {
privateFlagsExt &= ~PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
}
}
}

View File

@@ -37,6 +37,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethod;
import android.view.inputmethod.InputMethodSession;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
@@ -194,7 +195,9 @@ class IInputMethodWrapper extends IInputMethod.Stub
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
final IBinder startInputToken = (IBinder) args.arg1;
final IInputContext inputContext = (IInputContext) args.arg2;
final IInputContext inputContext = (IInputContext) ((SomeArgs) args.arg2).arg1;
final ImeOnBackInvokedDispatcher imeDispatcher =
(ImeOnBackInvokedDispatcher) ((SomeArgs) args.arg2).arg2;
final EditorInfo info = (EditorInfo) args.arg3;
final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4;
final boolean restarting = args.argi5 == 1;
@@ -205,7 +208,7 @@ class IInputMethodWrapper extends IInputMethod.Stub
: null;
info.makeCompatible(mTargetSdkVersion);
inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken,
navButtonFlags);
navButtonFlags, imeDispatcher);
args.recycle();
return;
}
@@ -348,13 +351,17 @@ class IInputMethodWrapper extends IInputMethod.Stub
@Override
public void startInput(IBinder startInputToken, IInputContext inputContext,
EditorInfo attribute, boolean restarting,
@InputMethodNavButtonFlags int navButtonFlags) {
@InputMethodNavButtonFlags int navButtonFlags,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (mCancellationGroup == null) {
Log.e(TAG, "startInput must be called after bindInput.");
mCancellationGroup = new CancellationGroup();
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = inputContext;
args.arg2 = imeDispatcher;
mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken,
inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
args, attribute, mCancellationGroup, restarting ? 1 : 0, navButtonFlags));
}
@BinderThread

View File

@@ -101,6 +101,7 @@ import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver;
import android.view.Choreographer;
import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEventReceiver;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
@@ -134,6 +135,9 @@ import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.window.ImeOnBackInvokedDispatcher;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowMetricsHelper;
import com.android.internal.annotations.GuardedBy;
@@ -344,6 +348,9 @@ public class InputMethodService extends AbstractInputMethodService {
* A circular buffer of size MAX_EVENTS_BUFFER in case IME is taking too long to add ink view.
**/
private RingBuffer<MotionEvent> mPendingEvents;
private ImeOnBackInvokedDispatcher mImeDispatcher;
private Boolean mBackCallbackRegistered = false;
private final OnBackInvokedCallback mCompatBackCallback = this::compatHandleBack;
/**
* Returns whether {@link InputMethodService} is responsible for rendering the back button and
@@ -797,7 +804,13 @@ public class InputMethodService extends AbstractInputMethodService {
@Override
public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
@NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
mImeDispatcher = imeDispatcher;
if (mWindow != null) {
mWindow.getOnBackInvokedDispatcher().setImeOnBackInvokedDispatcher(
imeDispatcher);
}
mPrivOps.reportStartInputAsync(startInputToken);
mNavigationBarController.onNavButtonFlagsChanged(navButtonFlags);
if (restarting) {
@@ -1496,6 +1509,10 @@ public class InputMethodService extends AbstractInputMethodService {
Context.LAYOUT_INFLATER_SERVICE);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow");
mWindow = new SoftInputWindow(this, mTheme, mDispatcherState);
if (mImeDispatcher != null) {
mWindow.getOnBackInvokedDispatcher()
.setImeOnBackInvokedDispatcher(mImeDispatcher);
}
mNavigationBarController.onSoftInputWindowCreated(mWindow);
{
final Window window = mWindow.getWindow();
@@ -1608,6 +1625,8 @@ public class InputMethodService extends AbstractInputMethodService {
// when IME developers are doing something unsupported.
InputMethodPrivilegedOperationsRegistry.remove(mToken);
}
unregisterCompatOnBackInvokedCallback();
mImeDispatcher = null;
}
/**
@@ -2568,9 +2587,47 @@ public class InputMethodService extends AbstractInputMethodService {
cancelImeSurfaceRemoval();
mInShowWindow = false;
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
registerCompatOnBackInvokedCallback();
}
/**
* Registers an {@link OnBackInvokedCallback} to handle back invocation when ahead-of-time
* back dispatching is enabled. We keep the {@link KeyEvent#KEYCODE_BACK} based legacy code
* around to handle back on older devices.
*/
private void registerCompatOnBackInvokedCallback() {
if (mBackCallbackRegistered) {
return;
}
if (mWindow != null) {
mWindow.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatBackCallback);
mBackCallbackRegistered = true;
}
}
private void unregisterCompatOnBackInvokedCallback() {
if (!mBackCallbackRegistered) {
return;
}
if (mWindow != null) {
mWindow.getOnBackInvokedDispatcher()
.unregisterOnBackInvokedCallback(mCompatBackCallback);
mBackCallbackRegistered = false;
}
}
private KeyEvent createBackKeyEvent(int action, boolean isTracking) {
final long when = SystemClock.uptimeMillis();
return new KeyEvent(when, when, action,
KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY
| (isTracking ? KeyEvent.FLAG_TRACKING : 0),
InputDevice.SOURCE_KEYBOARD);
}
private boolean prepareWindow(boolean showInput) {
boolean doShowInput = false;
mDecorViewVisible = true;
@@ -2658,6 +2715,7 @@ public class InputMethodService extends AbstractInputMethodService {
}
mLastWasInFullscreenMode = mIsFullscreen;
updateFullscreenMode();
unregisterCompatOnBackInvokedCallback();
}
/**
@@ -3797,4 +3855,14 @@ public class InputMethodService extends AbstractInputMethodService {
proto.end(token);
}
};
private void compatHandleBack() {
final KeyEvent downEvent = createBackKeyEvent(
KeyEvent.ACTION_DOWN, false /* isTracking */);
onKeyDown(KeyEvent.KEYCODE_BACK, downEvent);
final boolean hasStartedTracking =
(downEvent.getFlags() & KeyEvent.FLAG_START_TRACKING) != 0;
final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
}
}

View File

@@ -6095,6 +6095,28 @@ public final class ViewRootImpl implements ViewParent,
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
final KeyEvent event = (KeyEvent) q.mEvent;
// If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
// view tree or IME, and invoke the appropriate {@link OnBackInvokedCallback}.
if (isBack(event)
&& mContext != null
&& WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
OnBackInvokedCallback topCallback =
getOnBackInvokedDispatcher().getTopCallback();
if (event.getAction() == KeyEvent.ACTION_UP) {
if (topCallback != null) {
topCallback.onBackInvoked();
return FINISH_HANDLED;
}
} else {
// Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
return FINISH_NOT_HANDLED;
}
}
}
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
@@ -6446,24 +6468,6 @@ public final class ViewRootImpl implements ViewParent,
return FINISH_HANDLED;
}
// If the new back dispatch is enabled, intercept KEYCODE_BACK before it reaches the
// view tree and invoke the appropriate {@link OnBackInvokedCallback}.
if (isBack(event)
&& mContext != null
&& WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) {
OnBackInvokedCallback topCallback =
getOnBackInvokedDispatcher().getTopCallback();
if (event.getAction() == KeyEvent.ACTION_UP) {
if (topCallback != null) {
topCallback.onBackInvoked();
return FINISH_HANDLED;
}
} else {
// Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
return FINISH_NOT_HANDLED;
}
}
// Deliver the key to the view hierarchy.
if (mView.dispatchKeyEvent(event)) {
return FINISH_HANDLED;

View File

@@ -29,6 +29,7 @@ import android.util.Log;
import android.view.InputChannel;
import android.view.MotionEvent;
import android.view.View;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -232,6 +233,10 @@ public interface InputMethod {
* long as your implementation of {@link InputMethod} relies on such
* IPCs
* @param navButtonFlags {@link InputMethodNavButtonFlags} in the initial state of this session.
* @param imeDispatcher The {@link ImeOnBackInvokedDispatcher }} to be set on the
* IME's {@link android.window.WindowOnBackInvokedDispatcher}, so that IME
* {@link android.window.OnBackInvokedCallback}s can be forwarded to
* the client requesting to start input.
* @see #startInput(InputConnection, EditorInfo)
* @see #restartInput(InputConnection, EditorInfo)
* @see EditorInfo
@@ -240,7 +245,8 @@ public interface InputMethod {
@MainThread
default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection,
@NonNull EditorInfo editorInfo, boolean restarting,
@NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags) {
@NonNull IBinder startInputToken, @InputMethodNavButtonFlags int navButtonFlags,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (restarting) {
restartInput(inputConnection, editorInfo);
} else {

View File

@@ -91,6 +91,8 @@ import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.autofill.AutofillManager;
import android.window.ImeOnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
@@ -105,6 +107,7 @@ import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.os.SomeArgs;
import com.android.internal.view.IInputContext;
import com.android.internal.view.IInputMethodClient;
import com.android.internal.view.IInputMethodManager;
import com.android.internal.view.IInputMethodSession;
@@ -278,6 +281,21 @@ public final class InputMethodManager {
*/
private static final String SUBTYPE_MODE_VOICE = "voice";
/**
* Provide this to {@link IInputMethodManager#startInputOrWindowGainedFocus(
* int, IInputMethodClient, IBinder, int, int, int, EditorInfo, IInputContext, int)} to receive
* {@link android.window.OnBackInvokedCallback} registrations from IME.
*/
private final ImeOnBackInvokedDispatcher mImeDispatcher =
new ImeOnBackInvokedDispatcher(Handler.getMain()) {
@Override
public WindowOnBackInvokedDispatcher getReceivingDispatcher() {
synchronized (mH) {
return mCurRootView != null ? mCurRootView.getOnBackInvokedDispatcher() : null;
}
}
};
/**
* Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
* or indirectly relied on {@link #sInstance} via reflection or something like that.
@@ -740,7 +758,8 @@ public final class InputMethodManager {
windowFlags,
null,
null, null,
mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
mImeDispatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1687,6 +1706,8 @@ public final class InputMethodManager {
mServedConnecting = false;
clearConnectionLocked();
}
// Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
mImeDispatcher.clear();
}
public void displayCompletions(View view, CompletionInfo[] completions) {
@@ -2359,7 +2380,8 @@ public final class InputMethodManager {
softInputMode, windowFlags, tba, servedInputConnection,
servedInputConnection == null ? null
: servedInputConnection.asIRemoteAccessibilityInputConnection(),
view.getContext().getApplicationInfo().targetSdkVersion);
view.getContext().getApplicationInfo().targetSdkVersion,
mImeDispatcher);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -0,0 +1,20 @@
/* //device/java/android/android/os/ParcelFileDescriptor.aidl
**
** Copyright 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;
parcelable ImeOnBackInvokedDispatcher;

View File

@@ -0,0 +1,187 @@
/*
* 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;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.util.Log;
import java.util.HashMap;
/**
* A {@link OnBackInvokedDispatcher} for IME that forwards {@link OnBackInvokedCallback}
* registrations from the IME process to the app process to be registered on the app window.
* <p>
* The app process creates and propagates an instance of {@link ImeOnBackInvokedDispatcher}
* to the IME to be set on the IME window's {@link WindowOnBackInvokedDispatcher}.
* <p>
* @see WindowOnBackInvokedDispatcher#setImeOnBackInvokedDispatcher
*
* @hide
*/
public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parcelable {
private static final String TAG = "ImeBackDispatcher";
static final String RESULT_KEY_ID = "id";
static final String RESULT_KEY_CALLBACK = "callback";
static final String RESULT_KEY_PRIORITY = "priority";
static final int RESULT_CODE_REGISTER = 0;
static final int RESULT_CODE_UNREGISTER = 1;
@NonNull
private final ResultReceiver mResultReceiver;
public ImeOnBackInvokedDispatcher(Handler handler) {
mResultReceiver = new ResultReceiver(handler) {
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
WindowOnBackInvokedDispatcher dispatcher = getReceivingDispatcher();
if (dispatcher != null) {
receive(resultCode, resultData, dispatcher);
}
}
};
}
/**
* Override this method to return the {@link WindowOnBackInvokedDispatcher} of the window
* that should receive the forwarded callback.
*/
@Nullable
protected WindowOnBackInvokedDispatcher getReceivingDispatcher() {
return null;
}
ImeOnBackInvokedDispatcher(Parcel in) {
mResultReceiver = in.readTypedObject(ResultReceiver.CREATOR);
}
@Override
public void registerOnBackInvokedCallback(
@OnBackInvokedDispatcher.Priority int priority,
@NonNull OnBackInvokedCallback callback) {
final Bundle bundle = new Bundle();
final IOnBackInvokedCallback iCallback =
new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback);
bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder());
bundle.putInt(RESULT_KEY_PRIORITY, priority);
bundle.putInt(RESULT_KEY_ID, callback.hashCode());
mResultReceiver.send(RESULT_CODE_REGISTER, bundle);
}
@Override
public void unregisterOnBackInvokedCallback(
@NonNull OnBackInvokedCallback callback) {
Bundle bundle = new Bundle();
bundle.putInt(RESULT_KEY_ID, callback.hashCode());
mResultReceiver.send(RESULT_CODE_UNREGISTER, bundle);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeTypedObject(mResultReceiver, flags);
}
@NonNull
public static final Parcelable.Creator<ImeOnBackInvokedDispatcher> CREATOR =
new Parcelable.Creator<ImeOnBackInvokedDispatcher>() {
public ImeOnBackInvokedDispatcher createFromParcel(Parcel in) {
return new ImeOnBackInvokedDispatcher(in);
}
public ImeOnBackInvokedDispatcher[] newArray(int size) {
return new ImeOnBackInvokedDispatcher[size];
}
};
private final HashMap<Integer, OnBackInvokedCallback> mImeCallbackMap = new HashMap<>();
private void receive(
int resultCode, Bundle resultData,
@NonNull OnBackInvokedDispatcher receivingDispatcher) {
final int callbackId = resultData.getInt(RESULT_KEY_ID);
if (resultCode == RESULT_CODE_REGISTER) {
int priority = resultData.getInt(RESULT_KEY_PRIORITY);
final IOnBackInvokedCallback callback = IOnBackInvokedCallback.Stub.asInterface(
resultData.getBinder(RESULT_KEY_CALLBACK));
registerReceivedCallback(
callback, priority, callbackId, receivingDispatcher);
} else if (resultCode == RESULT_CODE_UNREGISTER) {
unregisterReceivedCallback(callbackId, receivingDispatcher);
}
}
private void registerReceivedCallback(
@NonNull IOnBackInvokedCallback iCallback,
@OnBackInvokedDispatcher.Priority int priority,
int callbackId,
@NonNull OnBackInvokedDispatcher receivingDispatcher) {
final ImeOnBackInvokedCallback imeCallback =
new ImeOnBackInvokedCallback(iCallback);
mImeCallbackMap.put(callbackId, imeCallback);
receivingDispatcher.registerOnBackInvokedCallback(priority, imeCallback);
}
private void unregisterReceivedCallback(
int callbackId, OnBackInvokedDispatcher receivingDispatcher) {
final OnBackInvokedCallback callback = mImeCallbackMap.get(callbackId);
if (callback == null) {
Log.e(TAG, "Ime callback not found. Ignoring unregisterReceivedCallback. "
+ "callbackId: " + callbackId);
return;
}
receivingDispatcher.unregisterOnBackInvokedCallback(callback);
}
/** Clears all registered callbacks on the instance. */
public void clear() {
mImeCallbackMap.clear();
}
static class ImeOnBackInvokedCallback implements OnBackInvokedCallback {
@NonNull
private final IOnBackInvokedCallback mIOnBackInvokedCallback;
ImeOnBackInvokedCallback(@NonNull IOnBackInvokedCallback iCallback) {
mIOnBackInvokedCallback = iCallback;
}
@Override
public void onBackInvoked() {
try {
if (mIOnBackInvokedCallback != null) {
mIOnBackInvokedCallback.onBackInvoked();
}
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
}
IOnBackInvokedCallback getIOnBackInvokedCallback() {
return mIOnBackInvokedCallback;
}
}
}

View File

@@ -96,4 +96,19 @@ public interface OnBackInvokedDispatcher {
* @hide
*/
default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
/**
* Sets an {@link ImeOnBackInvokedDispatcher} to forward {@link OnBackInvokedCallback}s
* from IME to the app process to be registered on the app window.
*
* Only call this on the IME window. Create the {@link ImeOnBackInvokedDispatcher} from
* the application process and override
* {@link ImeOnBackInvokedDispatcher#getReceivingDispatcher()} to point to the app
* window's {@link WindowOnBackInvokedDispatcher}.
*
* @hide
*/
default void setImeOnBackInvokedDispatcher(
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) { }
}

View File

@@ -49,6 +49,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>();
private final Object mLock = new Object();
private OnBackInvokedDispatcher mActualDispatcher = null;
private ImeOnBackInvokedDispatcher mImeDispatcher;
@Override
public void registerOnBackInvokedCallback(
@@ -108,6 +109,9 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
Log.v(TAG, String.format("Proxy transferring %d callbacks to %s", mCallbacks.size(),
mActualDispatcher));
}
if (mImeDispatcher != null) {
mActualDispatcher.setImeOnBackInvokedDispatcher(mImeDispatcher);
}
for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
int priority = callbackPair.second;
if (priority >= 0) {
@@ -117,6 +121,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
}
mCallbacks.clear();
mImeDispatcher = null;
}
private void clearCallbacksOnDispatcher() {
@@ -142,6 +147,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
synchronized (mLock) {
mCallbacks.clear();
mImeDispatcher = null;
}
}
@@ -169,4 +175,14 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
transferCallbacksToDispatcher();
}
}
@Override
public void setImeOnBackInvokedDispatcher(
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (mActualDispatcher != null) {
mActualDispatcher.setImeOnBackInvokedDispatcher(imeDispatcher);
} else {
mImeDispatcher = imeDispatcher;
}
}
}

View File

@@ -55,6 +55,8 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
.getInt("persist.wm.debug.predictive_back", 1) != 0;
private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
.getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
@Nullable
private ImeOnBackInvokedDispatcher mImeDispatcher;
/** Convenience hashmap to quickly decide if a callback has been added. */
private final HashMap<OnBackInvokedCallback, Integer> mAllCallbacks = new HashMap<>();
@@ -94,6 +96,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private void registerOnBackInvokedCallbackUnchecked(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
if (mImeDispatcher != null) {
mImeDispatcher.registerOnBackInvokedCallback(priority, callback);
return;
}
if (!mOnBackInvokedCallbacks.containsKey(priority)) {
mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
}
@@ -120,6 +126,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
@Override
public void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
if (mImeDispatcher != null) {
mImeDispatcher.unregisterOnBackInvokedCallback(callback);
return;
}
if (!mAllCallbacks.containsKey(callback)) {
if (DEBUG) {
Log.i(TAG, "Callback not found. returning...");
@@ -153,6 +163,9 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
}
mAllCallbacks.clear();
mOnBackInvokedCallbacks.clear();
if (mImeDispatcher != null) {
mImeDispatcher = null;
}
}
private void setTopOnBackInvokedCallback(@Nullable OnBackInvokedCallback callback) {
@@ -160,14 +173,18 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
return;
}
try {
if (callback == null) {
mWindowSession.setOnBackInvokedCallbackInfo(mWindow, null);
} else {
OnBackInvokedCallbackInfo callbackInfo = null;
if (callback != null) {
int priority = mAllCallbacks.get(callback);
mWindowSession.setOnBackInvokedCallbackInfo(
mWindow, new OnBackInvokedCallbackInfo(
new OnBackInvokedCallbackWrapper(callback), priority));
final IOnBackInvokedCallback iCallback =
callback instanceof ImeOnBackInvokedDispatcher
.ImeOnBackInvokedCallback
? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback)
callback).getIOnBackInvokedCallback()
: new OnBackInvokedCallbackWrapper(callback);
callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority);
}
mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo);
if (DEBUG && callback == null) {
Log.d(TAG, TextUtils.formatSimple("setTopOnBackInvokedCallback(null) Callers:%s",
Debug.getCallers(5, " ")));
@@ -190,7 +207,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
return null;
}
private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
private final WeakReference<OnBackInvokedCallback> mCallback;
OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
@@ -270,4 +287,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
return featureFlagEnabled && (appRequestsPredictiveBack || ALWAYS_ENFORCE_PREDICTIVE_BACK);
}
@Override
public void setImeOnBackInvokedDispatcher(
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
mImeDispatcher = imeDispatcher;
}
}

View File

@@ -23,6 +23,7 @@ import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.view.IInlineSuggestionsRequestCallback;
import com.android.internal.view.IInputContext;
@@ -47,7 +48,8 @@ oneway interface IInputMethod {
void unbindInput();
void startInput(in IBinder startInputToken, in IInputContext inputContext,
in EditorInfo attribute, boolean restarting, int navigationBarFlags);
in EditorInfo attribute, boolean restarting, int navigationBarFlags,
in ImeOnBackInvokedDispatcher imeDispatcher);
void onNavButtonFlagsChanged(int navButtonFlags);

View File

@@ -20,6 +20,7 @@ import android.os.ResultReceiver;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.inputmethod.EditorInfo;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
@@ -57,7 +58,7 @@ interface IInputMethodManager {
/* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
int windowFlags, in EditorInfo attribute, in IInputContext inputContext,
in IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion);
int unverifiedTargetSdkVersion, in ImeOnBackInvokedDispatcher imeDispatcher);
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);

View File

@@ -30,6 +30,7 @@ import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputBinding;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodNavButtonFlags;
@@ -148,10 +149,11 @@ final class IInputMethodInvoker {
@AnyThread
void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute,
boolean restarting, @InputMethodNavButtonFlags int navButtonFlags) {
boolean restarting, @InputMethodNavButtonFlags int navButtonFlags,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
try {
mTarget.startInput(startInputToken, inputContext, attribute, restarting,
navButtonFlags);
navButtonFlags, imeDispatcher);
} catch (RemoteException e) {
logRemoteException(e);
}

View File

@@ -150,6 +150,7 @@ import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTr
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.window.ImeOnBackInvokedDispatcher;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
@@ -615,6 +616,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
*/
IInputContext mCurInputContext;
/**
* The {@link ImeOnBackInvokedDispatcher} last provided by the current client to
* receive {@link android.window.OnBackInvokedCallback}s forwarded from IME.
*/
ImeOnBackInvokedDispatcher mCurImeDispatcher;
/**
* The {@link IRemoteAccessibilityInputConnection} last provided by the current client.
*/
@@ -2623,7 +2630,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
final SessionState session = mCurClient.curSession;
setEnabledSessionLocked(session);
session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting,
navButtonFlags);
navButtonFlags, mCurImeDispatcher);
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null,
@@ -2733,7 +2740,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
@NonNull EditorInfo attribute, @StartInputFlags int startInputFlags,
@StartInputReason int startInputReason,
int unverifiedTargetSdkVersion) {
int unverifiedTargetSdkVersion,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
// If no method is currently selected, do nothing.
final String selectedMethodId = getSelectedMethodIdLocked();
if (selectedMethodId == null) {
@@ -2777,6 +2785,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
mCurClient = cs;
mCurInputContext = inputContext;
mCurRemoteAccessibilityInputConnection = remoteAccessibilityInputConnection;
mCurImeDispatcher = imeDispatcher;
mCurVirtualDisplayToScreenMatrix =
getVirtualDisplayToScreenMatrixLocked(cs.selfReportedDisplayId,
mDisplayIdToShowIme);
@@ -3780,10 +3789,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
int unverifiedTargetSdkVersion,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
return startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
startInputFlags, softInputMode, windowFlags, attribute, inputContext,
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion);
remoteAccessibilityInputConnection, unverifiedTargetSdkVersion,
imeDispatcher);
}
@NonNull
@@ -3792,7 +3803,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
int windowFlags, @Nullable EditorInfo attribute, @Nullable IInputContext inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion) {
int unverifiedTargetSdkVersion,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (windowToken == null) {
Slog.e(TAG, "windowToken cannot be null.");
return InputBindResult.NULL;
@@ -3829,7 +3841,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
client, windowToken, startInputFlags, softInputMode, windowFlags,
attribute, inputContext, remoteAccessibilityInputConnection,
unverifiedTargetSdkVersion, userId);
unverifiedTargetSdkVersion, userId, imeDispatcher);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3857,7 +3869,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
@SoftInputModeFlags int softInputMode, int windowFlags, EditorInfo attribute,
IInputContext inputContext,
@Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
int unverifiedTargetSdkVersion, @UserIdInt int userId) {
int unverifiedTargetSdkVersion, @UserIdInt int userId,
@NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
if (DEBUG) {
Slog.v(TAG, "startInputOrWindowGainedFocusInternalLocked: reason="
+ InputMethodDebug.startInputReasonToString(startInputReason)
@@ -3868,7 +3881,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
+ InputMethodDebug.startInputFlagsToString(startInputFlags)
+ " softInputMode=" + InputMethodDebug.softInputModeToString(softInputMode)
+ " windowFlags=#" + Integer.toHexString(windowFlags)
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion);
+ " unverifiedTargetSdkVersion=" + unverifiedTargetSdkVersion
+ " imeDispatcher=" + imeDispatcher);
}
final ClientState cs = mClients.get(client.asBinder());
@@ -3952,7 +3966,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (attribute != null) {
return startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
startInputReason, unverifiedTargetSdkVersion, imeDispatcher);
}
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_REPORT_WINDOW_FOCUS_ONLY,
@@ -3993,7 +4007,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (isTextEditor && attribute != null
&& shouldRestoreImeVisibility(windowToken, softInputMode)) {
res = startInputUncheckedLocked(cs, inputContext, remoteAccessibilityInputConnection,
attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion);
attribute, startInputFlags, startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
SoftInputShowHideReason.SHOW_RESTORE_IME_VISIBILITY);
return res;
@@ -4033,7 +4048,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4065,7 +4081,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4085,7 +4102,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
if (attribute != null) {
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
didStart = true;
}
showCurrentInputLocked(windowToken, InputMethodManager.SHOW_IMPLICIT, null,
@@ -4115,7 +4133,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
}
res = startInputUncheckedLocked(cs, inputContext,
remoteAccessibilityInputConnection, attribute, startInputFlags,
startInputReason, unverifiedTargetSdkVersion);
startInputReason, unverifiedTargetSdkVersion,
imeDispatcher);
} else {
res = InputBindResult.NULL_EDITOR_INFO;
}

View File

@@ -187,7 +187,6 @@ class BackNavigationController {
if (backType == BackNavigationInfo.TYPE_CALLBACK
|| currentActivity == null
|| currentTask == null
|| currentTask.getDisplayContent().getImeContainer().isVisible()
|| currentActivity.isActivityTypeHome()) {
return infoBuilder
.setType(backType)