Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa5d4a89 authored by Antonio Kantek's avatar Antonio Kantek Committed by Android (Google) Code Review
Browse files

Merge "TouchMode (5/n) Implementing onTouchModeChanged"

parents 8db8d91c 48755989
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -174,10 +174,10 @@ public abstract class InputEventReceiver {
     * Called when the display for the window associated with the input channel has entered or
     * Called when the display for the window associated with the input channel has entered or
     * exited touch mode.
     * 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.
     *                                  input channel entered touch mode.
     */
     */
    public void onTouchModeChanged(boolean isInTouchMode) {
    public void onTouchModeChanged(boolean inTouchMode) {
    }
    }


    /**
    /**
+39 −8
Original line number Original line Diff line number Diff line
@@ -3369,14 +3369,12 @@ public final class ViewRootImpl implements ViewParent,


    private void handleWindowFocusChanged() {
    private void handleWindowFocusChanged() {
        final boolean hasWindowFocus;
        final boolean hasWindowFocus;
        final boolean inTouchMode;
        synchronized (this) {
        synchronized (this) {
            if (!mWindowFocusChanged) {
            if (!mWindowFocusChanged) {
                return;
                return;
            }
            }
            mWindowFocusChanged = false;
            mWindowFocusChanged = false;
            hasWindowFocus = mUpcomingWindowFocus;
            hasWindowFocus = mUpcomingWindowFocus;
            inTouchMode = mUpcomingInTouchMode;
        }
        }
        // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
        // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
        // config changes.
        // config changes.
@@ -3389,9 +3387,7 @@ public final class ViewRootImpl implements ViewParent,


        if (mAdded) {
        if (mAdded) {
            profileRendering(hasWindowFocus);
            profileRendering(hasWindowFocus);

            if (hasWindowFocus) {
            if (hasWindowFocus) {
                ensureTouchModeLocally(inTouchMode);
                if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
                if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
                    mFullRedrawNeeded = true;
                    mFullRedrawNeeded = true;
                    try {
                    try {
@@ -3463,6 +3459,14 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
    }
    }


    private void handleWindowTouchModeChanged() {
        final boolean inTouchMode;
        synchronized (this) {
            inTouchMode = mUpcomingInTouchMode;
        }
        ensureTouchModeLocally(inTouchMode);
    }

    private void fireAccessibilityFocusEventIfHasFocusedNode() {
    private void fireAccessibilityFocusEventIfHasFocusedNode() {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
        if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
            return;
            return;
@@ -5120,6 +5124,7 @@ public final class ViewRootImpl implements ViewParent,
    private static final int MSG_SHOW_INSETS = 34;
    private static final int MSG_SHOW_INSETS = 34;
    private static final int MSG_HIDE_INSETS = 35;
    private static final int MSG_HIDE_INSETS = 35;
    private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
    private static final int MSG_REQUEST_SCROLL_CAPTURE = 36;
    private static final int MSG_WINDOW_TOUCH_MODE_CHANGED = 37;




    final class ViewRootHandler extends Handler {
    final class ViewRootHandler extends Handler {
@@ -5186,6 +5191,8 @@ public final class ViewRootImpl implements ViewParent,
                    return "MSG_SHOW_INSETS";
                    return "MSG_SHOW_INSETS";
                case MSG_HIDE_INSETS:
                case MSG_HIDE_INSETS:
                    return "MSG_HIDE_INSETS";
                    return "MSG_HIDE_INSETS";
                case MSG_WINDOW_TOUCH_MODE_CHANGED:
                    return "MSG_WINDOW_TOUCH_MODE_CHANGED";
            }
            }
            return super.getMessageName(message);
            return super.getMessageName(message);
        }
        }
@@ -5308,9 +5315,12 @@ public final class ViewRootImpl implements ViewParent,
                case MSG_WINDOW_FOCUS_CHANGED: {
                case MSG_WINDOW_FOCUS_CHANGED: {
                    handleWindowFocusChanged();
                    handleWindowFocusChanged();
                } break;
                } break;
                case MSG_DIE:
                case MSG_WINDOW_TOUCH_MODE_CHANGED: {
                    handleWindowTouchModeChanged();
                } break;
                case MSG_DIE: {
                    doDie();
                    doDie();
                    break;
                } break;
                case MSG_DISPATCH_INPUT_EVENT: {
                case MSG_DISPATCH_INPUT_EVENT: {
                    SomeArgs args = (SomeArgs) msg.obj;
                    SomeArgs args = (SomeArgs) msg.obj;
                    InputEvent event = (InputEvent) args.arg1;
                    InputEvent event = (InputEvent) args.arg1;
@@ -8680,6 +8690,11 @@ public final class ViewRootImpl implements ViewParent,
            windowFocusChanged(hasFocus, inTouchMode);
            windowFocusChanged(hasFocus, inTouchMode);
        }
        }


        @Override
        public void onTouchModeChanged(boolean inTouchMode) {
            touchModeChanged(inTouchMode);
        }

        @Override
        @Override
        public void onPointerCaptureEvent(boolean pointerCaptureEnabled) {
        public void onPointerCaptureEvent(boolean pointerCaptureEnabled) {
            dispatchPointerCaptureChanged(pointerCaptureEnabled);
            dispatchPointerCaptureChanged(pointerCaptureEnabled);
@@ -8938,17 +8953,33 @@ public final class ViewRootImpl implements ViewParent,
        mHandler.sendMessage(msg);
        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): Delete unused inTouchMode parameter here once fully removing touch
        // mode status from focus event.
        synchronized (this) {
        synchronized (this) {
            mWindowFocusChanged = true;
            mWindowFocusChanged = true;
            mUpcomingWindowFocus = hasFocus;
            mUpcomingWindowFocus = hasFocus;
            mUpcomingInTouchMode = inTouchMode;
        }
        }
        Message msg = Message.obtain();
        Message msg = Message.obtain();
        msg.what = MSG_WINDOW_FOCUS_CHANGED;
        msg.what = MSG_WINDOW_FOCUS_CHANGED;
        mHandler.sendMessage(msg);
        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() {
    public void dispatchWindowShown() {
        mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
        mHandler.sendEmptyMessage(MSG_DISPATCH_WINDOW_SHOWN);
    }
    }
+70 −22
Original line number Original line Diff line number Diff line
@@ -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_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
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.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assert.fail;


import android.app.Instrumentation;
import android.content.Context;
import android.content.Context;
import android.os.Binder;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
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.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.platform.app.InstrumentationRegistry;


import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runner.RunWith;


@@ -66,15 +70,32 @@ import java.util.concurrent.TimeUnit;
public class ViewRootImplTest {
public class ViewRootImplTest {


    private ViewRootImpl mViewRootImpl;
    private ViewRootImpl mViewRootImpl;
    private Context mContext;
    private volatile boolean mKeyReceived = false;
    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
    @Before
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        mContext = getInstrumentation().getTargetContext();
        sInstrumentation.setInTouchMode(true);

        sInstrumentation.runOnMainSync(() ->
        getInstrumentation().runOnMainSync(() ->
                mViewRootImpl = new ViewRootImpl(sContext, sContext.getDisplayNoVerify()));
                mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
    }
    }


    @Test
    @Test
@@ -224,9 +245,9 @@ public class ViewRootImplTest {
     */
     */
    @Test
    @Test
    public void requestScrollCapture_timeout() {
    public void requestScrollCapture_timeout() {
        final View view = new View(mContext);
        final View view = new View(sContext);
        view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
        view.setScrollCaptureCallback(new TestScrollCaptureCallback()); // Does nothing
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
        sInstrumentation.runOnMainSync(() -> {
            WindowManager.LayoutParams wmlp =
            WindowManager.LayoutParams wmlp =
                    new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
                    new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
            // Set a fake token to bypass 'is your activity running' check
            // Set a fake token to bypass 'is your activity running' check
@@ -250,6 +271,29 @@ public class ViewRootImplTest {
        } catch (InterruptedException e) { /* ignore */ }
        } 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.
     * 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.
     * Next, inject an event into this view, and check whether it is received.
     */
     */
    private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
    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);
        attachViewToWindow(view);
        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();
        mViewRootImpl = view.getViewRootImpl();
        InstrumentationRegistry.getInstrumentation().runOnMainSync(setup);
        sInstrumentation.runOnMainSync(setup);
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        sInstrumentation.waitForIdleSync();


        // Inject a key event, and wait for it to be processed
        // 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);
            KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
            mViewRootImpl.dispatchInputEvent(event);
            mViewRootImpl.dispatchInputEvent(event);
        });
        });
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
        sInstrumentation.waitForIdleSync();
        assertEquals(mKeyReceived, shouldReceiveKey);
        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();
    }
}
}