| /* |
| * Copyright (C) 2018 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.statusbar.notification.stack; |
| |
| import static junit.framework.Assert.assertEquals; |
| import static junit.framework.Assert.assertFalse; |
| import static junit.framework.Assert.assertNotNull; |
| import static junit.framework.Assert.assertTrue; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.atLeastOnce; |
| import static org.mockito.Mockito.clearInvocations; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.mockitoSession; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.animation.Animator; |
| import android.animation.ValueAnimator.AnimatorUpdateListener; |
| import android.content.Context; |
| import android.graphics.Rect; |
| import android.os.Handler; |
| import android.os.IPowerManager; |
| import android.os.Looper; |
| import android.os.PowerManager; |
| import android.service.notification.StatusBarNotification; |
| import android.support.test.annotation.UiThreadTest; |
| import android.support.test.filters.SmallTest; |
| import android.support.test.runner.AndroidJUnit4; |
| import android.testing.TestableLooper.RunWithLooper; |
| import android.view.MotionEvent; |
| import android.view.VelocityTracker; |
| import android.view.View; |
| import android.view.MotionEvent; |
| |
| import com.android.systemui.SwipeHelper; |
| import com.android.systemui.SysuiTestCase; |
| import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; |
| import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; |
| import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; |
| import com.android.systemui.statusbar.notification.row.NotificationMenuRow; |
| |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoSession; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.junit.MockitoJUnit; |
| import org.mockito.junit.MockitoRule; |
| import org.mockito.stubbing.Answer; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Tests for {@link NotificationSwipeHelper}. |
| */ |
| @SmallTest |
| @RunWith(AndroidJUnit4.class) |
| public class NotificationSwipeHelperTest extends SysuiTestCase { |
| |
| private NotificationSwipeHelper mSwipeHelper; |
| private NotificationSwipeHelper.NotificationCallback mCallback; |
| private NotificationMenuRowPlugin.OnMenuEventListener mListener; |
| private View mView; |
| private MotionEvent mEvent; |
| private NotificationMenuRowPlugin mMenuRow; |
| private Handler mHandler; |
| private ExpandableNotificationRow mNotificationRow; |
| private Runnable mFalsingCheck; |
| |
| @Rule public MockitoRule mockito = MockitoJUnit.rule(); |
| |
| @Before |
| @UiThreadTest |
| public void setUp() throws Exception { |
| mCallback = mock(NotificationSwipeHelper.NotificationCallback.class); |
| mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class); |
| mSwipeHelper = spy(new NotificationSwipeHelper(SwipeHelper.X, mCallback, mContext, mListener)); |
| mView = mock(View.class); |
| mEvent = mock(MotionEvent.class); |
| mMenuRow = mock(NotificationMenuRowPlugin.class); |
| mNotificationRow = mock(ExpandableNotificationRow.class); |
| mHandler = mock(Handler.class); |
| mFalsingCheck = mock(Runnable.class); |
| } |
| |
| @Test |
| public void testSetExposedMenuView() { |
| assertEquals("intialized with null exposed menu view", null, |
| mSwipeHelper.getExposedMenuView()); |
| mSwipeHelper.setExposedMenuView(mView); |
| assertEquals("swipe helper has correct exposedMenuView after setExposedMenuView to a view", |
| mView, mSwipeHelper.getExposedMenuView()); |
| mSwipeHelper.setExposedMenuView(null); |
| assertEquals("swipe helper has null exposedMenuView after setExposedMenuView to null", |
| null, mSwipeHelper.getExposedMenuView()); |
| } |
| |
| @Test |
| public void testClearExposedMenuView() { |
| doNothing().when(mSwipeHelper).setExposedMenuView(mView); |
| mSwipeHelper.clearExposedMenuView(); |
| verify(mSwipeHelper, times(1)).setExposedMenuView(null); |
| } |
| |
| @Test |
| public void testGetTranslatingParentView() { |
| assertEquals("intialized with null translating parent view", null, |
| mSwipeHelper.getTranslatingParentView()); |
| mSwipeHelper.setTranslatingParentView(mView); |
| assertEquals("has translating parent view after setTranslatingParentView with a view", |
| mView, mSwipeHelper.getTranslatingParentView()); |
| } |
| |
| @Test |
| public void testClearTranslatingParentView() { |
| doNothing().when(mSwipeHelper).setTranslatingParentView(null); |
| mSwipeHelper.clearTranslatingParentView(); |
| verify(mSwipeHelper, times(1)).setTranslatingParentView(null); |
| } |
| |
| @Test |
| public void testSetCurrentMenuRow() { |
| assertEquals("currentMenuRow initializes to null", null, |
| mSwipeHelper.getCurrentMenuRow()); |
| mSwipeHelper.setCurrentMenuRow(mMenuRow); |
| assertEquals("currentMenuRow set correctly after setCurrentMenuRow", mMenuRow, |
| mSwipeHelper.getCurrentMenuRow()); |
| mSwipeHelper.setCurrentMenuRow(null); |
| assertEquals("currentMenuRow set to null after setCurrentMenuRow to null", |
| null, mSwipeHelper.getCurrentMenuRow()); |
| } |
| |
| @Test |
| public void testClearCurrentMenuRow() { |
| doNothing().when(mSwipeHelper).setCurrentMenuRow(null); |
| mSwipeHelper.clearCurrentMenuRow(); |
| verify(mSwipeHelper, times(1)).setCurrentMenuRow(null); |
| } |
| |
| @Test |
| public void testOnDownUpdate_ExpandableNotificationRow() { |
| when(mSwipeHelper.getHandler()).thenReturn(mHandler); |
| when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); |
| doNothing().when(mSwipeHelper).resetExposedMenuView(true, false); |
| doNothing().when(mSwipeHelper).clearCurrentMenuRow(); |
| doNothing().when(mSwipeHelper).initializeRow(any()); |
| |
| mSwipeHelper.onDownUpdate(mNotificationRow, mEvent); |
| |
| verify(mSwipeHelper, times(1)).clearCurrentMenuRow(); |
| verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); |
| verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false); |
| verify(mSwipeHelper, times(1)).initializeRow(mNotificationRow); |
| } |
| |
| @Test |
| public void testOnDownUpdate_notExpandableNotificationRow() { |
| when(mSwipeHelper.getHandler()).thenReturn(mHandler); |
| when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); |
| doNothing().when(mSwipeHelper).resetExposedMenuView(true, false); |
| doNothing().when(mSwipeHelper).clearCurrentMenuRow(); |
| doNothing().when(mSwipeHelper).initializeRow(any()); |
| |
| mSwipeHelper.onDownUpdate(mView, mEvent); |
| |
| verify(mSwipeHelper, times(1)).clearCurrentMenuRow(); |
| verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); |
| verify(mSwipeHelper, times(1)).resetExposedMenuView(true, false); |
| verify(mSwipeHelper, times(0)).initializeRow(any()); |
| } |
| |
| @Test |
| public void testOnMoveUpdate_menuRow() { |
| when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); |
| when(mSwipeHelper.getHandler()).thenReturn(mHandler); |
| when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); |
| |
| mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10); |
| |
| verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); |
| verify(mMenuRow, times(1)).onTouchMove(10); |
| } |
| |
| @Test |
| public void testOnMoveUpdate_noMenuRow() { |
| when(mSwipeHelper.getHandler()).thenReturn(mHandler); |
| when(mSwipeHelper.getFalsingCheck()).thenReturn(mFalsingCheck); |
| |
| mSwipeHelper.onMoveUpdate(mView, mEvent, 0, 10); |
| |
| verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); |
| } |
| |
| @Test |
| public void testHandleUpEvent_noMenuRow() { |
| assertFalse("Menu row does not exist", |
| mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); |
| } |
| |
| @Test |
| public void testHandleUpEvent_menuRow() { |
| when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); |
| doNothing().when(mSwipeHelper).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); |
| |
| assertTrue("Menu row exists", |
| mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); |
| verify(mMenuRow, times(1)).onTouchEnd(); |
| verify(mSwipeHelper, times(1)).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); |
| } |
| |
| @Test |
| public void testDismissChild_notExpanded() { |
| when(mCallback.isExpanded()).thenReturn(false); |
| doNothing().when(mSwipeHelper).superDismissChild(mView, 0, false); |
| doNothing().when(mSwipeHelper).handleMenuCoveredOrDismissed(); |
| |
| mSwipeHelper.dismissChild(mView, 0, false); |
| |
| verify(mSwipeHelper, times(1)).superDismissChild(mView, 0, false); |
| verify(mCallback, times(0)).handleChildViewDismissed(mView); |
| verify(mCallback, times(1)).onDismiss(); |
| verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed(); |
| } |
| |
| @Test |
| public void testSnapchild_targetIsZero() { |
| doNothing().when(mSwipeHelper).superSnapChild(mView, 0, 0); |
| mSwipeHelper.snapChild(mView, 0, 0); |
| |
| verify(mCallback, times(1)).onDragCancelled(mView); |
| verify(mSwipeHelper, times(1)).superSnapChild(mView, 0, 0); |
| verify(mSwipeHelper, times(1)).handleMenuCoveredOrDismissed(); |
| } |
| |
| |
| @Test |
| public void testSnapchild_targetNotZero() { |
| doNothing().when(mSwipeHelper).superSnapChild(mView, 10, 0); |
| mSwipeHelper.snapChild(mView, 10, 0); |
| |
| verify(mCallback, times(1)).onDragCancelled(mView); |
| verify(mSwipeHelper, times(1)).superSnapChild(mView, 10, 0); |
| verify(mSwipeHelper, times(0)).handleMenuCoveredOrDismissed(); |
| } |
| |
| @Test |
| public void testSnooze() { |
| StatusBarNotification sbn = mock(StatusBarNotification.class); |
| SnoozeOption snoozeOption = mock(SnoozeOption.class); |
| mSwipeHelper.snooze(sbn, snoozeOption); |
| verify(mCallback, times(1)).onSnooze(sbn, snoozeOption); |
| } |
| |
| @Test |
| public void testGetViewTranslationAnimator_notExpandableNotificationRow() { |
| Animator animator = mock(Animator.class); |
| AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); |
| doReturn(animator).when(mSwipeHelper).superGetViewTranslationAnimator(mView, 0, listener); |
| |
| assertEquals("returns the correct animator from super", animator, |
| mSwipeHelper.getViewTranslationAnimator(mView, 0, listener)); |
| |
| verify(mSwipeHelper, times(1)).superGetViewTranslationAnimator(mView, 0, listener); |
| } |
| |
| @Test |
| public void testGetViewTranslationAnimator_expandableNotificationRow() { |
| Animator animator = mock(Animator.class); |
| AnimatorUpdateListener listener = mock(AnimatorUpdateListener.class); |
| doReturn(animator).when(mNotificationRow).getTranslateViewAnimator(0, listener); |
| |
| assertEquals("returns the correct animator from super when view is an ENR", animator, |
| mSwipeHelper.getViewTranslationAnimator(mNotificationRow, 0, listener)); |
| |
| verify(mNotificationRow, times(1)).getTranslateViewAnimator(0, listener); |
| } |
| |
| @Test |
| public void testSetTranslation() { |
| mSwipeHelper.setTranslation(mNotificationRow, 0); |
| verify(mNotificationRow, times(1)).setTranslation(0); |
| } |
| |
| @Test |
| public void testGetTranslation() { |
| doReturn(30f).when(mNotificationRow).getTranslation(); |
| |
| assertEquals("Returns getTranslation for the ENR", |
| mSwipeHelper.getTranslation(mNotificationRow), 30f); |
| |
| verify(mNotificationRow, times(1)).getTranslation(); |
| } |
| |
| @Test |
| public void testDismiss() { |
| doNothing().when(mSwipeHelper).dismissChild(mView, 0, true); |
| doReturn(false).when(mSwipeHelper).swipedFastEnough(); |
| |
| mSwipeHelper.dismiss(mView, 0); |
| |
| verify(mSwipeHelper, times(1)).swipedFastEnough(); |
| verify(mSwipeHelper, times(1)).dismissChild(mView, 0, true); |
| } |
| |
| @Test |
| public void testSnapOpen() { |
| doNothing().when(mSwipeHelper).snapChild(mView, 30, 0); |
| |
| mSwipeHelper.snapOpen(mView, 30, 0); |
| |
| verify(mSwipeHelper, times(1)).snapChild(mView, 30, 0); |
| } |
| |
| @Test |
| public void testSnapClosed() { |
| doNothing().when(mSwipeHelper).snapChild(mView, 0, 0); |
| |
| mSwipeHelper.snapClosed(mView, 0); |
| |
| verify(mSwipeHelper, times(1)).snapChild(mView, 0, 0); |
| } |
| |
| @Test |
| public void testGetMinDismissVelocity() { |
| doReturn(30f).when(mSwipeHelper).getEscapeVelocity(); |
| |
| assertEquals("Returns getEscapeVelocity", 30f, mSwipeHelper.getMinDismissVelocity()); |
| } |
| |
| @Test |
| public void onMenuShown_noAntiFalsing() { |
| doNothing().when(mSwipeHelper).setExposedMenuView(mView); |
| doReturn(mView).when(mSwipeHelper).getTranslatingParentView(); |
| doReturn(mHandler).when(mSwipeHelper).getHandler(); |
| doReturn(false).when(mCallback).isAntiFalsingNeeded(); |
| doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck(); |
| |
| mSwipeHelper.onMenuShown(mView); |
| |
| verify(mSwipeHelper, times(1)).setExposedMenuView(mView); |
| verify(mCallback, times(1)).onDragCancelled(mView); |
| verify(mCallback, times(1)).isAntiFalsingNeeded(); |
| |
| verify(mHandler, times(0)).removeCallbacks(mFalsingCheck); |
| verify(mHandler, times(0)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY); |
| } |
| |
| @Test |
| public void onMenuShown_antiFalsing() { |
| doNothing().when(mSwipeHelper).setExposedMenuView(mView); |
| doReturn(mView).when(mSwipeHelper).getTranslatingParentView(); |
| doReturn(mHandler).when(mSwipeHelper).getHandler(); |
| doReturn(true).when(mCallback).isAntiFalsingNeeded(); |
| doReturn(mFalsingCheck).when(mSwipeHelper).getFalsingCheck(); |
| |
| mSwipeHelper.onMenuShown(mView); |
| |
| verify(mSwipeHelper, times(1)).setExposedMenuView(mView); |
| verify(mCallback, times(1)).onDragCancelled(mView); |
| verify(mCallback, times(1)).isAntiFalsingNeeded(); |
| |
| verify(mHandler, times(1)).removeCallbacks(mFalsingCheck); |
| verify(mHandler, times(1)).postDelayed(mFalsingCheck, mSwipeHelper.COVER_MENU_DELAY); |
| } |
| |
| @Test |
| public void testResetExposedMenuView_noReset() { |
| doReturn(false).when(mSwipeHelper).shouldResetMenu(false); |
| doNothing().when(mSwipeHelper).clearExposedMenuView(); |
| |
| mSwipeHelper.resetExposedMenuView(false, false); |
| |
| verify(mSwipeHelper, times(1)).shouldResetMenu(false); |
| |
| // should not clear exposed menu row |
| verify(mSwipeHelper, times(0)).clearExposedMenuView(); |
| } |
| |
| @Test |
| public void testResetExposedMenuView_animate() { |
| Animator animator = mock(Animator.class); |
| |
| doReturn(true).when(mSwipeHelper).shouldResetMenu(false); |
| doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView(); |
| doReturn(false).when(mNotificationRow).isRemoved(); |
| doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null); |
| doNothing().when(mSwipeHelper).clearExposedMenuView(); |
| |
| mSwipeHelper.resetExposedMenuView(true, false); |
| |
| verify(mSwipeHelper, times(1)).shouldResetMenu(false); |
| |
| // should retrieve and start animator |
| verify(mSwipeHelper, times(1)).getViewTranslationAnimator(mNotificationRow, 0, null); |
| verify(animator, times(1)).start(); |
| |
| // should not reset translation on row directly |
| verify(mNotificationRow, times(0)).resetTranslation(); |
| |
| // should clear exposed menu row |
| verify(mSwipeHelper, times(1)).clearExposedMenuView(); |
| } |
| |
| |
| @Test |
| public void testResetExposedMenuView_noAnimate() { |
| Animator animator = mock(Animator.class); |
| |
| doReturn(true).when(mSwipeHelper).shouldResetMenu(false); |
| doReturn(mNotificationRow).when(mSwipeHelper).getExposedMenuView(); |
| doReturn(false).when(mNotificationRow).isRemoved(); |
| doReturn(animator).when(mSwipeHelper).getViewTranslationAnimator(mNotificationRow, 0, null); |
| doNothing().when(mSwipeHelper).clearExposedMenuView(); |
| |
| mSwipeHelper.resetExposedMenuView(false, false); |
| |
| verify(mSwipeHelper, times(1)).shouldResetMenu(false); |
| |
| // should not retrieve and start animator |
| verify(mSwipeHelper, times(0)).getViewTranslationAnimator(mNotificationRow, 0, null); |
| verify(animator, times(0)).start(); |
| |
| // should reset translation on row directly |
| verify(mNotificationRow, times(1)).resetTranslation(); |
| |
| // should clear exposed menu row |
| verify(mSwipeHelper, times(1)).clearExposedMenuView(); |
| } |
| |
| @Test |
| public void testIsTouchInView() { |
| assertEquals("returns false when view is null", false, |
| NotificationSwipeHelper.isTouchInView(mEvent, null)); |
| |
| doReturn(5f).when(mEvent).getRawX(); |
| doReturn(10f).when(mEvent).getRawY(); |
| |
| doReturn(20).when(mView).getWidth(); |
| doReturn(20).when(mView).getHeight(); |
| |
| Answer answer = (Answer) invocation -> { |
| int[] arr = invocation.getArgument(0); |
| arr[0] = 0; |
| arr[1] = 0; |
| return null; |
| }; |
| doAnswer(answer).when(mView).getLocationOnScreen(any()); |
| |
| assertTrue("Touch is within the view", |
| mSwipeHelper.isTouchInView(mEvent, mView)); |
| |
| doReturn(50f).when(mEvent).getRawX(); |
| |
| assertFalse("Touch is not within the view", |
| mSwipeHelper.isTouchInView(mEvent, mView)); |
| } |
| |
| @Test |
| public void testIsTouchInView_expandable() { |
| assertEquals("returns false when view is null", false, |
| NotificationSwipeHelper.isTouchInView(mEvent, null)); |
| |
| doReturn(5f).when(mEvent).getRawX(); |
| doReturn(10f).when(mEvent).getRawY(); |
| |
| doReturn(20).when(mNotificationRow).getWidth(); |
| doReturn(20).when(mNotificationRow).getActualHeight(); |
| |
| Answer answer = (Answer) invocation -> { |
| int[] arr = invocation.getArgument(0); |
| arr[0] = 0; |
| arr[1] = 0; |
| return null; |
| }; |
| doAnswer(answer).when(mNotificationRow).getLocationOnScreen(any()); |
| |
| assertTrue("Touch is within the view", |
| mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); |
| |
| doReturn(50f).when(mEvent).getRawX(); |
| |
| assertFalse("Touch is not within the view", |
| mSwipeHelper.isTouchInView(mEvent, mNotificationRow)); |
| } |
| } |