Support for OnBackAnimationCallback: added for 3-button navigation / hardware keys

The callback can be called as follows if it supports the `onBackStarted()` and `onBackCancelled()` methods:
- `onBackStarted()` is called when the back button is pressed (`ACTION_DOWN`) and `keyEvent.getRepeatCount()` is `0`.
- `onBackCancelled()` is called when the back button is released (`ACTION_UP`) and the `keyEvent.isCanceled()` is `true`.
- `onBackInvoked()` is called when the back button is released (`ACTION_UP`) and the `keyEvent.isCanceled()` is `false`.

Test: atest OnBackInvokedCallbackGestureTest
Bug: 278634019
Change-Id: Iba5a27e68607ce33c38dbbc818d66e8753688761
This commit is contained in:
omarmt
2023-05-02 14:19:55 +00:00
parent 3e1928ca21
commit 63a3d4d749

View File

@@ -200,8 +200,10 @@ import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.BackEvent;
import android.window.ClientWindowFrames;
import android.window.CompatOnBackInvokedCallback;
import android.window.OnBackAnimationCallback;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.ScreenCapture;
@@ -6508,6 +6510,7 @@ public final class ViewRootImpl implements ViewParent,
*/
final class NativePreImeInputStage extends AsyncInputStage
implements InputQueue.FinishedInputEventCallback {
public NativePreImeInputStage(InputStage next, String traceCounter) {
super(next, traceCounter);
}
@@ -6515,32 +6518,62 @@ public final class ViewRootImpl implements ViewParent,
@Override
protected int onProcess(QueuedInputEvent q) {
if (q.mEvent instanceof KeyEvent) {
final KeyEvent event = (KeyEvent) q.mEvent;
final KeyEvent keyEvent = (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)
if (isBack(keyEvent)
&& mContext != null
&& mOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled()) {
OnBackInvokedCallback topCallback =
getOnBackInvokedDispatcher().getTopCallback();
if (event.getAction() == KeyEvent.ACTION_UP) {
if (topCallback != null) {
return doOnBackKeyEvent(keyEvent);
}
if (mInputQueue != null) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
}
}
return FORWARD;
}
private int doOnBackKeyEvent(KeyEvent keyEvent) {
OnBackInvokedCallback topCallback = getOnBackInvokedDispatcher().getTopCallback();
if (topCallback instanceof OnBackAnimationCallback) {
final OnBackAnimationCallback animationCallback =
(OnBackAnimationCallback) topCallback;
switch (keyEvent.getAction()) {
case KeyEvent.ACTION_DOWN:
// ACTION_DOWN is emitted twice: once when the user presses the button,
// and again a few milliseconds later.
// Based on the result of `keyEvent.getRepeatCount()` we have:
// - 0 means the button was pressed.
// - 1 means the button continues to be pressed (long press).
if (keyEvent.getRepeatCount() == 0) {
animationCallback.onBackStarted(
new BackEvent(0, 0, 0f, BackEvent.EDGE_LEFT));
}
break;
case KeyEvent.ACTION_UP:
if (keyEvent.isCanceled()) {
animationCallback.onBackCancelled();
} else {
topCallback.onBackInvoked();
return FINISH_HANDLED;
}
break;
}
} else if (topCallback != null) {
if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
if (!keyEvent.isCanceled()) {
topCallback.onBackInvoked();
return FINISH_HANDLED;
} else {
// Drop other actions such as {@link KeyEvent.ACTION_DOWN}.
return FINISH_NOT_HANDLED;
Log.d(mTag, "Skip onBackInvoked(), reason: keyEvent.isCanceled=true");
}
}
}
if (mInputQueue != null && q.mEvent instanceof KeyEvent) {
mInputQueue.sendInputEvent(q.mEvent, q, true, this);
return DEFER;
}
return FORWARD;
return FINISH_NOT_HANDLED;
}
@Override