From 5d9adbcb7bbdb4545d0919091630076e4c3d40aa Mon Sep 17 00:00:00 2001 From: Antonio Kantek Date: Thu, 4 Nov 2021 16:36:13 +0000 Subject: [PATCH] Roll forward of "Revert "TouchMode (5/n) Implementing onTouchModeChanged"" This reverts commit 8e8e721f61625ae013da5fbae42f7f192083be04. Reason for revert: ViewRootImpl#windowFocusChanged to also change touch mode. Fixes: 173468133 Bug: 193718270 Test: atest FrameworksCoreTests CtsWindowManagerDeviceTestCases Change-Id: I21780b86574c395cd6c2c5c05a88078c050edb8c --- .../java/android/view/InputEventReceiver.java | 4 +- core/java/android/view/ViewRootImpl.java | 47 ++++++++-- .../android/internal/policy/PhoneWindow.java | 5 +- .../src/android/view/ViewRootImplTest.java | 92 ++++++++++++++----- 4 files changed, 114 insertions(+), 34 deletions(-) diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java index 2884d2279bfd2..2165f55fbd608 100644 --- a/core/java/android/view/InputEventReceiver.java +++ b/core/java/android/view/InputEventReceiver.java @@ -174,10 +174,10 @@ public abstract class InputEventReceiver { * Called when the display for the window associated with the input channel has entered or * exited touch mode. * - * @param isInTouchMode {@code true} if the display showing the window associated with the + * @param inTouchMode {@code true} if the display showing the window associated with the * input channel entered touch mode. */ - public void onTouchModeChanged(boolean isInTouchMode) { + public void onTouchModeChanged(boolean inTouchMode) { } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b8215b5a83472..78dc57a948406 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3376,14 +3376,12 @@ public final class ViewRootImpl implements ViewParent, private void handleWindowFocusChanged() { final boolean hasWindowFocus; - final boolean inTouchMode; synchronized (this) { if (!mWindowFocusChanged) { return; } mWindowFocusChanged = false; hasWindowFocus = mUpcomingWindowFocus; - inTouchMode = mUpcomingInTouchMode; } // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback // config changes. @@ -3396,9 +3394,7 @@ public final class ViewRootImpl implements ViewParent, if (mAdded) { profileRendering(hasWindowFocus); - if (hasWindowFocus) { - ensureTouchModeLocally(inTouchMode); if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { mFullRedrawNeeded = true; try { @@ -3470,6 +3466,14 @@ public final class ViewRootImpl implements ViewParent, } } + private void handleWindowTouchModeChanged() { + final boolean inTouchMode; + synchronized (this) { + inTouchMode = mUpcomingInTouchMode; + } + ensureTouchModeLocally(inTouchMode); + } + private void fireAccessibilityFocusEventIfHasFocusedNode() { if (!AccessibilityManager.getInstance(mContext).isEnabled()) { return; @@ -5127,6 +5131,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_SHOW_INSETS = 34; private static final int MSG_HIDE_INSETS = 35; private static final int MSG_REQUEST_SCROLL_CAPTURE = 36; + private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37; final class ViewRootHandler extends Handler { @@ -5193,6 +5198,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_SHOW_INSETS"; case MSG_HIDE_INSETS: return "MSG_HIDE_INSETS"; + case MSG_WINDOW_TOUCH_MODE_CHANGED: + return "MSG_WINDOW_TOUCH_MODE_CHANGED"; } return super.getMessageName(message); } @@ -5315,9 +5322,12 @@ public final class ViewRootImpl implements ViewParent, case MSG_WINDOW_FOCUS_CHANGED: { handleWindowFocusChanged(); } break; - case MSG_DIE: + case MSG_WINDOW_TOUCH_MODE_CHANGED: { + handleWindowTouchModeChanged(); + } break; + case MSG_DIE: { doDie(); - break; + } break; case MSG_DISPATCH_INPUT_EVENT: { SomeArgs args = (SomeArgs) msg.obj; InputEvent event = (InputEvent) args.arg1; @@ -8687,6 +8697,11 @@ public final class ViewRootImpl implements ViewParent, windowFocusChanged(hasFocus, inTouchMode); } + @Override + public void onTouchModeChanged(boolean inTouchMode) { + touchModeChanged(inTouchMode); + } + @Override public void onPointerCaptureEvent(boolean pointerCaptureEnabled) { dispatchPointerCaptureChanged(pointerCaptureEnabled); @@ -8945,17 +8960,33 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { + /** + * Notifies this {@link ViewRootImpl} object that window focus has changed. + */ + public void windowFocusChanged(boolean hasFocus, boolean unusedInTouchMode) { + // TODO(b/193718270): Remove inTouchMode parameter from this method and update related code + // accordingly. synchronized (this) { mWindowFocusChanged = true; mUpcomingWindowFocus = hasFocus; - mUpcomingInTouchMode = inTouchMode; } Message msg = Message.obtain(); msg.what = MSG_WINDOW_FOCUS_CHANGED; mHandler.sendMessage(msg); } + /** + * Notifies this {@link ViewRootImpl} object that touch mode state has changed. + */ + public void touchModeChanged(boolean inTouchMode) { + synchronized (this) { + mUpcomingInTouchMode = inTouchMode; + } + Message msg = Message.obtain(); + msg.what = MSG_WINDOW_TOUCH_MODE_CHANGED; + mHandler.sendMessage(msg); + } + public void dispatchWindowShown() { mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN); } diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index bfc57b403a417..b4289703378ff 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -1841,8 +1841,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setLocalFocus(boolean hasFocus, boolean inTouchMode) { - getViewRootImpl().windowFocusChanged(hasFocus, inTouchMode); - + ViewRootImpl viewRoot = getViewRootImpl(); + viewRoot.windowFocusChanged(hasFocus, inTouchMode); + viewRoot.touchModeChanged(inTouchMode); } @Override diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index e0d9ecfcb336f..882a0da2d97f0 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -31,12 +31,14 @@ 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; -import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.app.Instrumentation; import android.content.Context; import android.os.Binder; import android.platform.test.annotations.Presubmit; @@ -47,7 +49,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.AfterClass; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; @@ -58,7 +62,7 @@ import java.util.concurrent.TimeUnit; * Tests for {@link ViewRootImpl} * * Build/Install/Run: - * atest FrameworksCoreTests:ViewRootImplTest + * atest FrameworksCoreTests:ViewRootImplTest */ @Presubmit @SmallTest @@ -66,15 +70,32 @@ import java.util.concurrent.TimeUnit; public class ViewRootImplTest { private ViewRootImpl mViewRootImpl; - private Context mContext; private volatile boolean mKeyReceived = false; + private static Context sContext; + private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation(); + + // The touch mode state before the test was started, needed to return the system to the original + // state after the test completes. + private static boolean sOriginalTouchMode; + + @BeforeClass + public static void setUpClass() { + sContext = sInstrumentation.getTargetContext(); + View view = new View(sContext); + sOriginalTouchMode = view.isInTouchMode(); + } + + @AfterClass + public static void tearDownClass() { + sInstrumentation.setInTouchMode(sOriginalTouchMode); + } + @Before public void setUp() throws Exception { - mContext = getInstrumentation().getTargetContext(); - - getInstrumentation().runOnMainSync(() -> - mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify())); + sInstrumentation.setInTouchMode(true); + sInstrumentation.runOnMainSync(() -> + mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify())); } @Test @@ -224,9 +245,9 @@ public class ViewRootImplTest { */ @Test public void requestScrollCapture_timeout() { - final View view = new View(mContext); + final View view = new View(sContext); view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing - InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + sInstrumentation.runOnMainSync(() -> { WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); // Set a fake token to bypass 'is your activity running' check @@ -250,6 +271,29 @@ public class ViewRootImplTest { } catch (InterruptedException e) { /* ignore */ } } + @Test + public void whenTouchModeChanges_viewRootIsNotified() throws Exception { + View view = new View(sContext); + attachViewToWindow(view); + ViewTreeObserver viewTreeObserver = view.getRootView().getViewTreeObserver(); + CountDownLatch latch = new CountDownLatch(1); + ViewTreeObserver.OnTouchModeChangeListener touchModeListener = (boolean inTouchMode) -> { + assertWithMessage("addOnTouchModeChangeListener parameter").that( + inTouchMode).isFalse(); + latch.countDown(); + }; + viewTreeObserver.addOnTouchModeChangeListener(touchModeListener); + + try { + view.requestFocusFromTouch(); + + assertThat(latch.await(1, TimeUnit.SECONDS)).isTrue(); + assertThat(view.isInTouchMode()).isFalse(); + } finally { + viewTreeObserver.removeOnTouchModeChangeListener(touchModeListener); + } + } + /** * When window doesn't have focus, keys should be dropped. */ @@ -308,27 +352,31 @@ public class ViewRootImplTest { * 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); + final KeyView view = new KeyView(sContext); - 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(); + attachViewToWindow(view); mViewRootImpl = view.getViewRootImpl(); - InstrumentationRegistry.getInstrumentation().runOnMainSync(setup); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + sInstrumentation.runOnMainSync(setup); + sInstrumentation.waitForIdleSync(); // Inject a key event, and wait for it to be processed - InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + sInstrumentation.runOnMainSync(() -> { KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A); mViewRootImpl.dispatchInputEvent(event); }); - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + sInstrumentation.waitForIdleSync(); assertEquals(mKeyReceived, shouldReceiveKey); } + + private void attachViewToWindow(View view) { + WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY); + wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check + + sInstrumentation.runOnMainSync(() -> { + WindowManager wm = sContext.getSystemService(WindowManager.class); + wm.addView(view, wmlp); + }); + sInstrumentation.waitForIdleSync(); + } }