diff options
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 45 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewRootImplTest.java | 91 |
2 files changed, 118 insertions, 18 deletions
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 0fc2d7a75ee1..b1eedd8e4eec 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -5535,26 +5535,39 @@ public final class ViewRootImpl implements ViewParent, if (mView == null || !mAdded) { Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent); return true; - } else if ((!mAttachInfo.mHasWindowFocus + } + + // Find a reason for dropping or canceling the event. + final String reason; + if (!mAttachInfo.mHasWindowFocus && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER) - && !isAutofillUiShowing()) || mStopped - || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) - || (mPausedForTransition && !isBack(q.mEvent))) { - // This is a focus event and the window doesn't currently have input focus or - // has stopped. This could be an event that came back from the previous stage + && !isAutofillUiShowing()) { + // This is a non-pointer event and the window doesn't currently have input focus + // This could be an event that came back from the previous stage // but the window has lost focus or stopped in the meantime. - if (isTerminalInputEvent(q.mEvent)) { - // Don't drop terminal input events, however mark them as canceled. - q.mEvent.cancel(); - Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent); - return false; - } + reason = "no window focus"; + } else if (mStopped) { + reason = "window is stopped"; + } else if (mIsAmbientMode + && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) { + reason = "non-button event in ambient mode"; + } else if (mPausedForTransition && !isBack(q.mEvent)) { + reason = "paused for transition"; + } else { + // Most common path: no reason to drop or cancel the event + return false; + } - // Drop non-terminal input events. - Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent); - return true; + if (isTerminalInputEvent(q.mEvent)) { + // Don't drop terminal input events, however mark them as canceled. + q.mEvent.cancel(); + Slog.w(mTag, "Cancelling event (" + reason + "):" + q.mEvent); + return false; } - return false; + + // Drop non-terminal input events. + Slog.w(mTag, "Dropping event (" + reason + "):" + q.mEvent); + return true; } void dump(String prefix, PrintWriter writer) { diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index 4cf6715ba0ca..8fa212a59984 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -26,6 +26,7 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; @@ -35,12 +36,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Before; import org.junit.Test; @@ -58,13 +61,15 @@ import org.junit.runner.RunWith; public class ViewRootImplTest { private ViewRootImpl mViewRootImpl; + private Context mContext; + private volatile boolean mKeyReceived = false; @Before public void setUp() throws Exception { - final Context context = getInstrumentation().getTargetContext(); + mContext = getInstrumentation().getTargetContext(); getInstrumentation().runOnMainSync(() -> - mViewRootImpl = new ViewRootImpl(context, context.getDisplayNoVerify())); + mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify())); } @Test @@ -178,4 +183,86 @@ public class ViewRootImplTest { // setSystemBarsBehavior. assertEquals(behavior, controller.getSystemBarsBehavior()); } + + /** + * When window doesn't have focus, keys should be dropped. + */ + @Test + public void whenWindowDoesNotHaveFocus_keysAreDropped() { + checkKeyEvent(() -> { + mViewRootImpl.windowFocusChanged(false /*hasFocus*/, true /*inTouchMode*/); + }, false /*shouldReceiveKey*/); + } + + /** + * When window has focus, keys should be received + */ + @Test + public void whenWindowHasFocus_keysAreReceived() { + checkKeyEvent(() -> { + mViewRootImpl.windowFocusChanged(true /*hasFocus*/, true /*inTouchMode*/); + }, true /*shouldReceiveKey*/); + } + + /** + * When window is in ambient mode, keys should be dropped + */ + @Test + public void whenWindowIsInAmbientMode_keysAreDropped() { + checkKeyEvent(() -> { + mViewRootImpl.setIsAmbientMode(true /*ambient*/); + }, false /*shouldReceiveKey*/); + } + + /** + * When window is paused for transition, keys should be dropped + */ + @Test + public void whenWindowIsPausedForTransition_keysAreDropped() { + checkKeyEvent(() -> { + mViewRootImpl.setPausedForTransition(true /*paused*/); + }, false /*shouldReceiveKey*/); + } + + class KeyView extends View { + KeyView(Context context) { + super(context); + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + mKeyReceived = true; + return true /*handled*/; + } + } + + /** + * Create a new view, and add it to window manager. + * Run the precondition 'setup'. + * Next, inject an event into this view, and check whether it is received. + */ + private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) { + final KeyView view = new KeyView(mContext); + + WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowManager wm = mContext.getSystemService(WindowManager.class); + wm.addView(view, wmlp); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + mViewRootImpl = view.getViewRootImpl(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(setup); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + + // Inject a key event, and wait for it to be processed + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A); + mViewRootImpl.dispatchInputEvent(event); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + assertEquals(mKeyReceived, shouldReceiveKey); + } } |