Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +30 −5 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; Loading Loading @@ -119,6 +120,7 @@ public class PipMenuActivity extends Activity { } }; private PipTouchState mTouchState; private PointF mDownPosition = new PointF(); private PointF mDownDelta = new PointF(); private ViewConfiguration mViewConfig; Loading Loading @@ -175,6 +177,13 @@ public class PipMenuActivity extends Activity { // Set the flags to allow us to watch for outside touches and also hide the menu and start // manipulating the PIP in the same touch gesture mViewConfig = ViewConfiguration.get(this); mTouchState = new PipTouchState(mViewConfig, mHandler, () -> { if (mMenuState == MENU_STATE_CLOSE) { showPipMenu(); } else { expandPip(); } }); getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY); super.onCreate(savedInstanceState); Loading @@ -186,12 +195,28 @@ public class PipMenuActivity extends Activity { mViewRoot.setBackground(mBackgroundDrawable); mMenuContainer = findViewById(R.id.menu_container); mMenuContainer.setAlpha(0); mMenuContainer.setOnClickListener((v) -> { mMenuContainer.setOnTouchListener((v, event) -> { mTouchState.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: if (mTouchState.isDoubleTap() || mMenuState == MENU_STATE_FULL) { // Expand to fullscreen if this is a double tap or we are already expanded expandPip(); } else if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, // just expand the menu if necessary if (mMenuState == MENU_STATE_CLOSE) { showPipMenu(); } } else { expandPip(); // Next touch event _may_ be the second tap for the double-tap, schedule a // fallback runnable to trigger the menu if no touch event occurs before the // next tap mTouchState.scheduleDoubleTapTimeoutCallback(); } break; } return true; }); mDismissButton = findViewById(R.id.dismiss); mDismissButton.setAlpha(0); Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +4 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,10 @@ public class PipMenuActivityController { EventBus.getDefault().register(this); } public boolean isMenuActivityVisible() { return mToActivityMessenger != null; } public void onActivityPinned() { if (mMenuState == MENU_STATE_NONE) { // If the menu is not visible, then re-register the input consumer if it is not already Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +18 −4 Original line number Diff line number Diff line Loading @@ -187,13 +187,15 @@ public class PipTouchHandler { mMenuController.addListener(mMenuListener); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mTouchState = new PipTouchState(mViewConfig); mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f); mGestures = new PipTouchGesture[] { mDefaultMovementGesture }; mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController, mSnapAlgorithm, mFlingAnimationUtils); mTouchState = new PipTouchState(mViewConfig, mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); Resources res = context.getResources(); mExpandedShortestEdgeSize = res.getDimensionPixelSize( Loading Loading @@ -429,7 +431,7 @@ public class PipTouchHandler { final float distance = bounds.bottom - target; fraction = Math.min(distance / bounds.height(), 1f); } if (Float.compare(fraction, 0f) != 0 || mMenuState != MENU_STATE_NONE) { if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) { // Update if the fraction > 0, or if fraction == 0 and the menu was already visible mMenuController.setDismissFraction(fraction); } Loading Loading @@ -730,8 +732,20 @@ public class PipTouchHandler { null /* animatorListener */); setMinimizedStateInternal(false); } else if (mMenuState != MENU_STATE_FULL) { if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap mMotionHelper.expandPip(); } else if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, just // expand the menu mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } else { // Next touch event _may_ be the second tap for the double-tap, schedule a // fallback runnable to trigger the menu if no touch event occurs before the // next tap mTouchState.scheduleDoubleTapTimeoutCallback(); } } else { mMenuController.hideMenu(); mMotionHelper.expandPip(); Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java +68 −2 Original line number Diff line number Diff line Loading @@ -17,11 +17,15 @@ package com.android.systemui.pip.phone; import android.graphics.PointF; import android.os.Handler; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; /** Loading @@ -31,9 +35,17 @@ public class PipTouchState { private static final String TAG = "PipTouchHandler"; private static final boolean DEBUG = false; private ViewConfiguration mViewConfig; @VisibleForTesting static final long DOUBLE_TAP_TIMEOUT = 200; private final Handler mHandler; private final ViewConfiguration mViewConfig; private final Runnable mDoubleTapTimeoutCallback; private VelocityTracker mVelocityTracker; private long mDownTouchTime = 0; private long mLastDownTouchTime = 0; private long mUpTouchTime = 0; private final PointF mDownTouch = new PointF(); private final PointF mDownDelta = new PointF(); private final PointF mLastTouch = new PointF(); Loading @@ -41,13 +53,22 @@ public class PipTouchState { private final PointF mVelocity = new PointF(); private boolean mAllowTouches = true; private boolean mIsUserInteracting = false; // Set to true only if the multiple taps occur within the double tap timeout private boolean mIsDoubleTap = false; // Set to true only if a gesture private boolean mIsWaitingForDoubleTap = false; private boolean mIsDragging = false; // The previous gesture was a drag private boolean mPreviouslyDragging = false; private boolean mStartedDragging = false; private boolean mAllowDraggingOffscreen = false; private int mActivePointerId; public PipTouchState(ViewConfiguration viewConfig) { public PipTouchState(ViewConfiguration viewConfig, Handler handler, Runnable doubleTapTimeoutCallback) { mViewConfig = viewConfig; mHandler = handler; mDoubleTapTimeoutCallback = doubleTapTimeoutCallback; } /** Loading Loading @@ -81,6 +102,14 @@ public class PipTouchState { mDownTouch.set(mLastTouch); mAllowDraggingOffscreen = true; mIsUserInteracting = true; mDownTouchTime = ev.getEventTime(); mIsDoubleTap = !mPreviouslyDragging && (mDownTouchTime - mLastDownTouchTime) < DOUBLE_TAP_TIMEOUT; mIsWaitingForDoubleTap = false; mLastDownTouchTime = mDownTouchTime; if (mDoubleTapTimeoutCallback != null) { mHandler.removeCallbacks(mDoubleTapTimeoutCallback); } break; } case MotionEvent.ACTION_MOVE: { Loading Loading @@ -155,7 +184,11 @@ public class PipTouchState { break; } mUpTouchTime = ev.getEventTime(); mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); mPreviouslyDragging = mIsDragging; mIsWaitingForDoubleTap = !mIsDoubleTap && !mIsDragging && (mUpTouchTime - mDownTouchTime) < DOUBLE_TAP_TIMEOUT; // Fall through to clean up } Loading Loading @@ -251,6 +284,39 @@ public class PipTouchState { return mAllowDraggingOffscreen; } /** * @return whether this gesture is a double-tap. */ public boolean isDoubleTap() { return mIsDoubleTap; } /** * @return whether this gesture will potentially lead to a following double-tap. */ public boolean isWaitingForDoubleTap() { return mIsWaitingForDoubleTap; } /** * Schedules the callback to run if the next double tap does not occur. Only runs if * isWaitingForDoubleTap() is true. */ public void scheduleDoubleTapTimeoutCallback() { if (mIsWaitingForDoubleTap) { long delay = getDoubleTapTimeoutCallbackDelay(); mHandler.removeCallbacks(mDoubleTapTimeoutCallback); mHandler.postDelayed(mDoubleTapTimeoutCallback, delay); } } @VisibleForTesting long getDoubleTapTimeoutCallbackDelay() { if (mIsWaitingForDoubleTap) { return Math.max(0, DOUBLE_TAP_TIMEOUT - (mUpTouchTime - mDownTouchTime)); } return -1; } private void initOrResetVelocityTracker() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); Loading packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 com.android.systemui.pip.phone; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.SystemClock; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.pip.phone.PipTouchState; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest public class PipTouchStateTest extends SysuiTestCase { private Handler mHandler; private HandlerThread mHandlerThread; private PipTouchState mTouchState; private CountDownLatch mDoubleTapCallbackTriggeredLatch; @Before public void setUp() throws Exception { mHandlerThread = new HandlerThread("PipTouchStateTestThread"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1); mTouchState = new PipTouchState(ViewConfiguration.get(getContext()), mHandler, () -> { mDoubleTapCallbackTriggeredLatch.countDown(); }); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); } @Test public void testDoubleTapLongSingleTap_notDoubleTapAndNotWaiting() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT + 10, 0, 0)); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } @Test public void testDoubleTapTimeout_timeoutCallbackCalled() throws Exception { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 10, 0, 0)); assertFalse(mTouchState.isDoubleTap()); assertTrue(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10); mTouchState.scheduleDoubleTapTimeoutCallback(); mDoubleTapCallbackTriggeredLatch.await(1, TimeUnit.SECONDS); assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0); } @Test public void testDoubleTapDrag_doubleTapCanceled() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_MOVE, currentTime + 10, 500, 500)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + 20, 500, 500)); assertTrue(mTouchState.isDragging()); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } @Test public void testDoubleTap_doubleTapRegistered() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + 10, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 20, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 10, 0, 0)); assertTrue(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) { return MotionEvent.obtain(0, eventTime, action, x, y, 0); } } No newline at end of file Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +30 −5 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowManager.LayoutParams; Loading Loading @@ -119,6 +120,7 @@ public class PipMenuActivity extends Activity { } }; private PipTouchState mTouchState; private PointF mDownPosition = new PointF(); private PointF mDownDelta = new PointF(); private ViewConfiguration mViewConfig; Loading Loading @@ -175,6 +177,13 @@ public class PipMenuActivity extends Activity { // Set the flags to allow us to watch for outside touches and also hide the menu and start // manipulating the PIP in the same touch gesture mViewConfig = ViewConfiguration.get(this); mTouchState = new PipTouchState(mViewConfig, mHandler, () -> { if (mMenuState == MENU_STATE_CLOSE) { showPipMenu(); } else { expandPip(); } }); getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | LayoutParams.FLAG_SLIPPERY); super.onCreate(savedInstanceState); Loading @@ -186,12 +195,28 @@ public class PipMenuActivity extends Activity { mViewRoot.setBackground(mBackgroundDrawable); mMenuContainer = findViewById(R.id.menu_container); mMenuContainer.setAlpha(0); mMenuContainer.setOnClickListener((v) -> { mMenuContainer.setOnTouchListener((v, event) -> { mTouchState.onTouchEvent(event); switch (event.getAction()) { case MotionEvent.ACTION_UP: if (mTouchState.isDoubleTap() || mMenuState == MENU_STATE_FULL) { // Expand to fullscreen if this is a double tap or we are already expanded expandPip(); } else if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, // just expand the menu if necessary if (mMenuState == MENU_STATE_CLOSE) { showPipMenu(); } } else { expandPip(); // Next touch event _may_ be the second tap for the double-tap, schedule a // fallback runnable to trigger the menu if no touch event occurs before the // next tap mTouchState.scheduleDoubleTapTimeoutCallback(); } break; } return true; }); mDismissButton = findViewById(R.id.dismiss); mDismissButton.setAlpha(0); Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +4 −0 Original line number Diff line number Diff line Loading @@ -211,6 +211,10 @@ public class PipMenuActivityController { EventBus.getDefault().register(this); } public boolean isMenuActivityVisible() { return mToActivityMessenger != null; } public void onActivityPinned() { if (mMenuState == MENU_STATE_NONE) { // If the menu is not visible, then re-register the input consumer if it is not already Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +18 −4 Original line number Diff line number Diff line Loading @@ -187,13 +187,15 @@ public class PipTouchHandler { mMenuController.addListener(mMenuListener); mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mTouchState = new PipTouchState(mViewConfig); mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f); mGestures = new PipTouchGesture[] { mDefaultMovementGesture }; mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mMenuController, mSnapAlgorithm, mFlingAnimationUtils); mTouchState = new PipTouchState(mViewConfig, mHandler, () -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu())); Resources res = context.getResources(); mExpandedShortestEdgeSize = res.getDimensionPixelSize( Loading Loading @@ -429,7 +431,7 @@ public class PipTouchHandler { final float distance = bounds.bottom - target; fraction = Math.min(distance / bounds.height(), 1f); } if (Float.compare(fraction, 0f) != 0 || mMenuState != MENU_STATE_NONE) { if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) { // Update if the fraction > 0, or if fraction == 0 and the menu was already visible mMenuController.setDismissFraction(fraction); } Loading Loading @@ -730,8 +732,20 @@ public class PipTouchHandler { null /* animatorListener */); setMinimizedStateInternal(false); } else if (mMenuState != MENU_STATE_FULL) { if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap mMotionHelper.expandPip(); } else if (!mTouchState.isWaitingForDoubleTap()) { // User has stalled long enough for this not to be a drag or a double tap, just // expand the menu mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(), mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } else { // Next touch event _may_ be the second tap for the double-tap, schedule a // fallback runnable to trigger the menu if no touch event occurs before the // next tap mTouchState.scheduleDoubleTapTimeoutCallback(); } } else { mMenuController.hideMenu(); mMotionHelper.expandPip(); Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java +68 −2 Original line number Diff line number Diff line Loading @@ -17,11 +17,15 @@ package com.android.systemui.pip.phone; import android.graphics.PointF; import android.os.Handler; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; /** Loading @@ -31,9 +35,17 @@ public class PipTouchState { private static final String TAG = "PipTouchHandler"; private static final boolean DEBUG = false; private ViewConfiguration mViewConfig; @VisibleForTesting static final long DOUBLE_TAP_TIMEOUT = 200; private final Handler mHandler; private final ViewConfiguration mViewConfig; private final Runnable mDoubleTapTimeoutCallback; private VelocityTracker mVelocityTracker; private long mDownTouchTime = 0; private long mLastDownTouchTime = 0; private long mUpTouchTime = 0; private final PointF mDownTouch = new PointF(); private final PointF mDownDelta = new PointF(); private final PointF mLastTouch = new PointF(); Loading @@ -41,13 +53,22 @@ public class PipTouchState { private final PointF mVelocity = new PointF(); private boolean mAllowTouches = true; private boolean mIsUserInteracting = false; // Set to true only if the multiple taps occur within the double tap timeout private boolean mIsDoubleTap = false; // Set to true only if a gesture private boolean mIsWaitingForDoubleTap = false; private boolean mIsDragging = false; // The previous gesture was a drag private boolean mPreviouslyDragging = false; private boolean mStartedDragging = false; private boolean mAllowDraggingOffscreen = false; private int mActivePointerId; public PipTouchState(ViewConfiguration viewConfig) { public PipTouchState(ViewConfiguration viewConfig, Handler handler, Runnable doubleTapTimeoutCallback) { mViewConfig = viewConfig; mHandler = handler; mDoubleTapTimeoutCallback = doubleTapTimeoutCallback; } /** Loading Loading @@ -81,6 +102,14 @@ public class PipTouchState { mDownTouch.set(mLastTouch); mAllowDraggingOffscreen = true; mIsUserInteracting = true; mDownTouchTime = ev.getEventTime(); mIsDoubleTap = !mPreviouslyDragging && (mDownTouchTime - mLastDownTouchTime) < DOUBLE_TAP_TIMEOUT; mIsWaitingForDoubleTap = false; mLastDownTouchTime = mDownTouchTime; if (mDoubleTapTimeoutCallback != null) { mHandler.removeCallbacks(mDoubleTapTimeoutCallback); } break; } case MotionEvent.ACTION_MOVE: { Loading Loading @@ -155,7 +184,11 @@ public class PipTouchState { break; } mUpTouchTime = ev.getEventTime(); mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); mPreviouslyDragging = mIsDragging; mIsWaitingForDoubleTap = !mIsDoubleTap && !mIsDragging && (mUpTouchTime - mDownTouchTime) < DOUBLE_TAP_TIMEOUT; // Fall through to clean up } Loading Loading @@ -251,6 +284,39 @@ public class PipTouchState { return mAllowDraggingOffscreen; } /** * @return whether this gesture is a double-tap. */ public boolean isDoubleTap() { return mIsDoubleTap; } /** * @return whether this gesture will potentially lead to a following double-tap. */ public boolean isWaitingForDoubleTap() { return mIsWaitingForDoubleTap; } /** * Schedules the callback to run if the next double tap does not occur. Only runs if * isWaitingForDoubleTap() is true. */ public void scheduleDoubleTapTimeoutCallback() { if (mIsWaitingForDoubleTap) { long delay = getDoubleTapTimeoutCallbackDelay(); mHandler.removeCallbacks(mDoubleTapTimeoutCallback); mHandler.postDelayed(mDoubleTapTimeoutCallback, delay); } } @VisibleForTesting long getDoubleTapTimeoutCallbackDelay() { if (mIsWaitingForDoubleTap) { return Math.max(0, DOUBLE_TAP_TIMEOUT - (mUpTouchTime - mDownTouchTime)); } return -1; } private void initOrResetVelocityTracker() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); Loading
packages/SystemUI/tests/src/com/android/systemui/pip/phone/PipTouchStateTest.java 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 com.android.systemui.pip.phone; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.SystemClock; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.pip.phone.PipTouchState; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest public class PipTouchStateTest extends SysuiTestCase { private Handler mHandler; private HandlerThread mHandlerThread; private PipTouchState mTouchState; private CountDownLatch mDoubleTapCallbackTriggeredLatch; @Before public void setUp() throws Exception { mHandlerThread = new HandlerThread("PipTouchStateTestThread"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1); mTouchState = new PipTouchState(ViewConfiguration.get(getContext()), mHandler, () -> { mDoubleTapCallbackTriggeredLatch.countDown(); }); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); } @Test public void testDoubleTapLongSingleTap_notDoubleTapAndNotWaiting() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT + 10, 0, 0)); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } @Test public void testDoubleTapTimeout_timeoutCallbackCalled() throws Exception { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 10, 0, 0)); assertFalse(mTouchState.isDoubleTap()); assertTrue(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10); mTouchState.scheduleDoubleTapTimeoutCallback(); mDoubleTapCallbackTriggeredLatch.await(1, TimeUnit.SECONDS); assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0); } @Test public void testDoubleTapDrag_doubleTapCanceled() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_MOVE, currentTime + 10, 500, 500)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + 20, 500, 500)); assertTrue(mTouchState.isDragging()); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } @Test public void testDoubleTap_doubleTapRegistered() { final long currentTime = SystemClock.uptimeMillis(); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + 10, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_DOWN, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 20, 0, 0)); mTouchState.onTouchEvent(createMotionEvent(ACTION_UP, currentTime + PipTouchState.DOUBLE_TAP_TIMEOUT - 10, 0, 0)); assertTrue(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == -1); } private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) { return MotionEvent.obtain(0, eventTime, action, x, y, 0); } } No newline at end of file