Merge "Multi-shade foundation - integration (5/5)." into udc-dev am: 0003196b1a am: 65346cbefe
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21990414 Change-Id: I960166965ac98cba4cc6cf39178610217ffb5291 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
22
packages/SystemUI/res/layout/multi_shade.xml
Normal file
22
packages/SystemUI/res/layout/multi_shade.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2023 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.
|
||||
~
|
||||
-->
|
||||
|
||||
<com.android.systemui.multishade.ui.view.MultiShadeView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -110,6 +110,13 @@
|
||||
android:clipChildren="false"
|
||||
android:clipToPadding="false" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/multi_shade_stub"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:inflatedId="@+id/multi_shade"
|
||||
android:layout="@layout/multi_shade" />
|
||||
|
||||
<com.android.systemui.biometrics.AuthRippleView
|
||||
android:id="@+id/auth_ripple"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.multishade.ui.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.FrameLayout
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.android.systemui.compose.ComposeFacade
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
|
||||
import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel
|
||||
import com.android.systemui.util.time.SystemClock
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* View that hosts the multi-shade system and acts as glue between legacy code and the
|
||||
* implementation.
|
||||
*/
|
||||
class MultiShadeView(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
) :
|
||||
FrameLayout(
|
||||
context,
|
||||
attrs,
|
||||
) {
|
||||
|
||||
fun init(
|
||||
interactor: MultiShadeInteractor,
|
||||
clock: SystemClock,
|
||||
) {
|
||||
repeatWhenAttached {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
addView(
|
||||
ComposeFacade.createMultiShadeView(
|
||||
context = context,
|
||||
viewModel =
|
||||
MultiShadeViewModel(
|
||||
viewModelScope = this,
|
||||
interactor = interactor,
|
||||
),
|
||||
clock = clock,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Here when destroyed.
|
||||
removeAllViews()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import android.app.StatusBarManager;
|
||||
import android.media.AudioManager;
|
||||
import android.media.session.MediaSessionLegacyHelper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.InputDevice;
|
||||
@@ -31,6 +30,7 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.keyguard.AuthKeyguardMessageArea;
|
||||
@@ -39,8 +39,10 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
|
||||
import com.android.systemui.classifier.FalsingCollector;
|
||||
import com.android.systemui.compose.ComposeFacade;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
@@ -49,6 +51,8 @@ import com.android.systemui.keyguard.shared.model.TransitionStep;
|
||||
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
|
||||
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor;
|
||||
import com.android.systemui.multishade.ui.view.MultiShadeView;
|
||||
import com.android.systemui.statusbar.DragDownHelper;
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||
import com.android.systemui.statusbar.NotificationInsetsController;
|
||||
@@ -63,11 +67,13 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
||||
import com.android.systemui.util.time.SystemClock;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
/**
|
||||
* Controller for {@link NotificationShadeWindowView}.
|
||||
@@ -115,6 +121,7 @@ public class NotificationShadeWindowViewController {
|
||||
mIsOcclusionTransitionRunning =
|
||||
step.getTransitionState() == TransitionState.RUNNING;
|
||||
};
|
||||
private final SystemClock mClock;
|
||||
|
||||
@Inject
|
||||
public NotificationShadeWindowViewController(
|
||||
@@ -142,7 +149,9 @@ public class NotificationShadeWindowViewController {
|
||||
UdfpsOverlayInteractor udfpsOverlayInteractor,
|
||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
|
||||
FeatureFlags featureFlags) {
|
||||
FeatureFlags featureFlags,
|
||||
Provider<MultiShadeInteractor> multiShadeInteractorProvider,
|
||||
SystemClock clock) {
|
||||
mLockscreenShadeTransitionController = transitionController;
|
||||
mFalsingCollector = falsingCollector;
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
@@ -175,6 +184,16 @@ public class NotificationShadeWindowViewController {
|
||||
|
||||
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
|
||||
mLockscreenToDreamingTransition);
|
||||
|
||||
mClock = clock;
|
||||
if (ComposeFacade.INSTANCE.isComposeAvailable()
|
||||
&& featureFlags.isEnabled(Flags.DUAL_SHADE)) {
|
||||
final ViewStub multiShadeViewStub = mView.findViewById(R.id.multi_shade_stub);
|
||||
if (multiShadeViewStub != null) {
|
||||
final MultiShadeView multiShadeView = (MultiShadeView) multiShadeViewStub.inflate();
|
||||
multiShadeView.init(multiShadeInteractorProvider.get(), clock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +288,7 @@ public class NotificationShadeWindowViewController {
|
||||
mLockIconViewController.onTouchEvent(
|
||||
ev,
|
||||
() -> mService.wakeUpIfDozing(
|
||||
SystemClock.uptimeMillis(),
|
||||
mClock.uptimeMillis(),
|
||||
mView,
|
||||
"LOCK_ICON_TOUCH",
|
||||
PowerManager.WAKE_REASON_GESTURE)
|
||||
@@ -453,7 +472,7 @@ public class NotificationShadeWindowViewController {
|
||||
|
||||
public void cancelCurrentTouch() {
|
||||
if (mTouchActive) {
|
||||
final long now = SystemClock.uptimeMillis();
|
||||
final long now = mClock.uptimeMillis();
|
||||
final MotionEvent event;
|
||||
if (mIsTrackpadGestureBackEnabled) {
|
||||
event = MotionEvent.obtain(mDownEvent);
|
||||
|
||||
@@ -37,6 +37,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
|
||||
import com.android.systemui.keyguard.shared.model.TransitionStep
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
|
||||
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
|
||||
import com.android.systemui.multishade.data.repository.MultiShadeRepository
|
||||
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
|
||||
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
import com.android.systemui.statusbar.NotificationInsetsController
|
||||
@@ -50,8 +53,12 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -65,10 +72,12 @@ import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var view: NotificationShadeWindowView
|
||||
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
|
||||
@Mock private lateinit var centralSurfaces: CentralSurfaces
|
||||
@@ -102,6 +111,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var underTest: NotificationShadeWindowViewController
|
||||
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
@@ -115,8 +126,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
|
||||
.thenReturn(emptyFlow<TransitionStep>())
|
||||
|
||||
val featureFlags = FakeFeatureFlags();
|
||||
val featureFlags = FakeFeatureFlags()
|
||||
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
|
||||
featureFlags.set(Flags.DUAL_SHADE, false)
|
||||
|
||||
val inputProxy = MultiShadeInputProxy()
|
||||
testScope = TestScope()
|
||||
underTest =
|
||||
NotificationShadeWindowViewController(
|
||||
lockscreenShadeTransitionController,
|
||||
@@ -144,6 +159,18 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
keyguardTransitionInteractor,
|
||||
primaryBouncerToGoneTransitionViewModel,
|
||||
featureFlags,
|
||||
{
|
||||
MultiShadeInteractor(
|
||||
applicationScope = testScope.backgroundScope,
|
||||
repository =
|
||||
MultiShadeRepository(
|
||||
applicationContext = context,
|
||||
inputProxy = inputProxy,
|
||||
),
|
||||
inputProxy = inputProxy,
|
||||
)
|
||||
},
|
||||
FakeSystemClock(),
|
||||
)
|
||||
underTest.setupExpandedStatusBar()
|
||||
|
||||
@@ -156,147 +183,162 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||
// tests need to be added to test the rest of handleDispatchTouchEvent.
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
|
||||
underTest.setStatusBarViewController(null)
|
||||
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(null)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
assertThat(returnVal).isFalse()
|
||||
}
|
||||
assertThat(returnVal).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
|
||||
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev)
|
||||
|
||||
verify(phoneStatusBarViewController).sendTouchToView(ev)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
verify(phoneStatusBarViewController).sendTouchToView(ev)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
val downEvBelow =
|
||||
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
||||
interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
|
||||
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
val downEvBelow =
|
||||
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
||||
interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
|
||||
|
||||
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
||||
val nextEvent =
|
||||
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
||||
|
||||
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
|
||||
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
verify(phoneStatusBarViewController).sendTouchToView(downEv)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
verify(phoneStatusBarViewController).sendTouchToView(DOWN_EVENT)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
|
||||
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
|
||||
assertThat(returnVal).isNull()
|
||||
}
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||
assertThat(returnVal).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(false)
|
||||
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(false)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
|
||||
assertThat(returnVal).isNull()
|
||||
}
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||
assertThat(returnVal).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
|
||||
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
// Item we're testing
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() =
|
||||
testScope.runTest {
|
||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||
.thenReturn(true)
|
||||
|
||||
// Down event first
|
||||
interactionEventHandler.handleDispatchTouchEvent(downEv)
|
||||
// Down event first
|
||||
interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||
|
||||
// Then another event
|
||||
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
||||
// Then another event
|
||||
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
|
||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
||||
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
||||
|
||||
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||
assertThat(returnVal).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() {
|
||||
// Down event within udfpsOverlay bounds while alternateBouncer is showing
|
||||
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(downEv)).thenReturn(false)
|
||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
||||
fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() =
|
||||
testScope.runTest {
|
||||
// Down event within udfpsOverlay bounds while alternateBouncer is showing
|
||||
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(DOWN_EVENT))
|
||||
.thenReturn(false)
|
||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
||||
|
||||
// Then touch should not be intercepted
|
||||
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(downEv)
|
||||
assertThat(shouldIntercept).isFalse()
|
||||
}
|
||||
// Then touch should not be intercepted
|
||||
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
|
||||
assertThat(shouldIntercept).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetBouncerContainer() {
|
||||
Mockito.clearInvocations(view)
|
||||
underTest.bouncerContainer
|
||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
|
||||
}
|
||||
fun testGetBouncerContainer() =
|
||||
testScope.runTest {
|
||||
Mockito.clearInvocations(view)
|
||||
underTest.bouncerContainer
|
||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetKeyguardMessageArea() {
|
||||
underTest.keyguardMessageArea
|
||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
|
||||
fun testGetKeyguardMessageArea() =
|
||||
testScope.runTest {
|
||||
underTest.keyguardMessageArea
|
||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
|
||||
private const val VIEW_BOTTOM = 100
|
||||
}
|
||||
}
|
||||
|
||||
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
|
||||
private const val VIEW_BOTTOM = 100
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* 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.shade;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.keyguard.KeyguardSecurityContainerController;
|
||||
import com.android.keyguard.LockIconViewController;
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
|
||||
import com.android.systemui.classifier.FalsingCollectorFake;
|
||||
import com.android.systemui.dock.DockManager;
|
||||
import com.android.systemui.flags.FakeFeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
|
||||
import com.android.systemui.statusbar.DragDownHelper;
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||
import com.android.systemui.statusbar.NotificationInsetsController;
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController;
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController;
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController;
|
||||
import com.android.systemui.statusbar.notification.stack.AmbientState;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
public class NotificationShadeWindowViewTest extends SysuiTestCase {
|
||||
|
||||
private NotificationShadeWindowView mView;
|
||||
private NotificationShadeWindowViewController mController;
|
||||
|
||||
@Mock private TunerService mTunerService;
|
||||
@Mock private DragDownHelper mDragDownHelper;
|
||||
@Mock private SysuiStatusBarStateController mStatusBarStateController;
|
||||
@Mock private ShadeController mShadeController;
|
||||
@Mock private CentralSurfaces mCentralSurfaces;
|
||||
@Mock private DockManager mDockManager;
|
||||
@Mock private NotificationPanelViewController mNotificationPanelViewController;
|
||||
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
|
||||
@Mock private NotificationShadeDepthController mNotificationShadeDepthController;
|
||||
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
|
||||
@Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
|
||||
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
@Mock private StatusBarWindowStateController mStatusBarWindowStateController;
|
||||
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
|
||||
@Mock private LockIconViewController mLockIconViewController;
|
||||
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
|
||||
@Mock private AmbientState mAmbientState;
|
||||
@Mock private PulsingGestureListener mPulsingGestureListener;
|
||||
@Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
|
||||
@Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
|
||||
@Mock private KeyguardBouncerComponent mKeyguardBouncerComponent;
|
||||
@Mock private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
|
||||
@Mock private NotificationInsetsController mNotificationInsetsController;
|
||||
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
|
||||
@Mock private UdfpsOverlayInteractor mUdfpsOverlayInteractor;
|
||||
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
|
||||
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
|
||||
|
||||
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
|
||||
mInteractionEventHandlerCaptor;
|
||||
private NotificationShadeWindowView.InteractionEventHandler mInteractionEventHandler;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mView = spy(new NotificationShadeWindowView(getContext(), null));
|
||||
when(mView.findViewById(R.id.notification_stack_scroller))
|
||||
.thenReturn(mNotificationStackScrollLayout);
|
||||
|
||||
when(mView.findViewById(R.id.keyguard_bouncer_container)).thenReturn(mock(ViewGroup.class));
|
||||
when(mKeyguardBouncerComponentFactory.create(any(ViewGroup.class))).thenReturn(
|
||||
mKeyguardBouncerComponent);
|
||||
when(mKeyguardBouncerComponent.getSecurityContainerController()).thenReturn(
|
||||
mKeyguardSecurityContainerController);
|
||||
|
||||
when(mStatusBarStateController.isDozing()).thenReturn(false);
|
||||
mDependency.injectTestDependency(ShadeController.class, mShadeController);
|
||||
|
||||
when(mDockManager.isDocked()).thenReturn(false);
|
||||
|
||||
when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
|
||||
.thenReturn(emptyFlow());
|
||||
|
||||
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
|
||||
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false);
|
||||
mController = new NotificationShadeWindowViewController(
|
||||
mLockscreenShadeTransitionController,
|
||||
new FalsingCollectorFake(),
|
||||
mStatusBarStateController,
|
||||
mDockManager,
|
||||
mNotificationShadeDepthController,
|
||||
mView,
|
||||
mNotificationPanelViewController,
|
||||
new ShadeExpansionStateManager(),
|
||||
mNotificationStackScrollLayoutController,
|
||||
mStatusBarKeyguardViewManager,
|
||||
mStatusBarWindowStateController,
|
||||
mLockIconViewController,
|
||||
mCentralSurfaces,
|
||||
mNotificationShadeWindowController,
|
||||
mKeyguardUnlockAnimationController,
|
||||
mNotificationInsetsController,
|
||||
mAmbientState,
|
||||
mPulsingGestureListener,
|
||||
mKeyguardBouncerViewModel,
|
||||
mKeyguardBouncerComponentFactory,
|
||||
mAlternateBouncerInteractor,
|
||||
mUdfpsOverlayInteractor,
|
||||
mKeyguardTransitionInteractor,
|
||||
mPrimaryBouncerToGoneTransitionViewModel,
|
||||
featureFlags
|
||||
);
|
||||
mController.setupExpandedStatusBar();
|
||||
mController.setDragDownHelper(mDragDownHelper);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDragDownHelperCalledWhenDraggingDown() {
|
||||
when(mDragDownHelper.isDraggingDown()).thenReturn(true);
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
MotionEvent ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0 /* x */, 0 /* y */,
|
||||
0 /* meta */);
|
||||
mView.onTouchEvent(ev);
|
||||
verify(mDragDownHelper).onTouchEvent(ev);
|
||||
ev.recycle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterceptTouchWhenShowingAltAuth() {
|
||||
captureInteractionEventHandler();
|
||||
|
||||
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
when(mStatusBarStateController.isDozing()).thenReturn(false);
|
||||
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
|
||||
when(mUdfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true);
|
||||
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
|
||||
|
||||
// THEN we should intercept touch
|
||||
assertTrue(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoInterceptTouch() {
|
||||
captureInteractionEventHandler();
|
||||
|
||||
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
when(mStatusBarStateController.isDozing()).thenReturn(false);
|
||||
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
|
||||
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
|
||||
|
||||
// THEN we shouldn't intercept touch
|
||||
assertFalse(mInteractionEventHandler.shouldInterceptTouchEvent(mock(MotionEvent.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleTouchEventWhenShowingAltAuth() {
|
||||
captureInteractionEventHandler();
|
||||
|
||||
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
when(mStatusBarStateController.isDozing()).thenReturn(false);
|
||||
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
|
||||
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
|
||||
|
||||
// THEN we should handle the touch
|
||||
assertTrue(mInteractionEventHandler.handleTouchEvent(mock(MotionEvent.class)));
|
||||
}
|
||||
|
||||
private void captureInteractionEventHandler() {
|
||||
verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture());
|
||||
mInteractionEventHandler = mInteractionEventHandlerCaptor.getValue();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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.shade
|
||||
|
||||
import android.os.SystemClock
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.MotionEvent
|
||||
import android.widget.FrameLayout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardSecurityContainerController
|
||||
import com.android.keyguard.LockIconViewController
|
||||
import com.android.keyguard.dagger.KeyguardBouncerComponent
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
|
||||
import com.android.systemui.classifier.FalsingCollectorFake
|
||||
import com.android.systemui.dock.DockManager
|
||||
import com.android.systemui.flags.FakeFeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
|
||||
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
|
||||
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
|
||||
import com.android.systemui.multishade.data.repository.MultiShadeRepository
|
||||
import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor
|
||||
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||
import com.android.systemui.statusbar.DragDownHelper
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||
import com.android.systemui.statusbar.NotificationInsetsController
|
||||
import com.android.systemui.statusbar.NotificationShadeDepthController
|
||||
import com.android.systemui.statusbar.NotificationShadeWindowController
|
||||
import com.android.systemui.statusbar.SysuiStatusBarStateController
|
||||
import com.android.systemui.statusbar.notification.stack.AmbientState
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
|
||||
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
|
||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
@SmallTest
|
||||
class NotificationShadeWindowViewTest : SysuiTestCase() {
|
||||
|
||||
@Mock private lateinit var dragDownHelper: DragDownHelper
|
||||
@Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
|
||||
@Mock private lateinit var shadeController: ShadeController
|
||||
@Mock private lateinit var centralSurfaces: CentralSurfaces
|
||||
@Mock private lateinit var dockManager: DockManager
|
||||
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
|
||||
@Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
|
||||
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
|
||||
@Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
|
||||
@Mock
|
||||
private lateinit var notificationStackScrollLayoutController:
|
||||
NotificationStackScrollLayoutController
|
||||
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
|
||||
@Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
|
||||
@Mock
|
||||
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
|
||||
@Mock private lateinit var lockIconViewController: LockIconViewController
|
||||
@Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
|
||||
@Mock private lateinit var ambientState: AmbientState
|
||||
@Mock private lateinit var pulsingGestureListener: PulsingGestureListener
|
||||
@Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
|
||||
@Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
|
||||
@Mock private lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
|
||||
@Mock
|
||||
private lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
|
||||
@Mock private lateinit var notificationInsetsController: NotificationInsetsController
|
||||
@Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
|
||||
@Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
|
||||
@Mock
|
||||
private lateinit var primaryBouncerToGoneTransitionViewModel:
|
||||
PrimaryBouncerToGoneTransitionViewModel
|
||||
@Captor
|
||||
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
|
||||
@Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
|
||||
|
||||
private lateinit var underTest: NotificationShadeWindowView
|
||||
private lateinit var controller: NotificationShadeWindowViewController
|
||||
private lateinit var interactionEventHandler: InteractionEventHandler
|
||||
private lateinit var testScope: TestScope
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
underTest = spy(NotificationShadeWindowView(context, null))
|
||||
whenever(
|
||||
underTest.findViewById<NotificationStackScrollLayout>(
|
||||
R.id.notification_stack_scroller
|
||||
)
|
||||
)
|
||||
.thenReturn(notificationStackScrollLayout)
|
||||
whenever(underTest.findViewById<FrameLayout>(R.id.keyguard_bouncer_container))
|
||||
.thenReturn(mock())
|
||||
whenever(keyguardBouncerComponentFactory.create(any())).thenReturn(keyguardBouncerComponent)
|
||||
whenever(keyguardBouncerComponent.securityContainerController)
|
||||
.thenReturn(keyguardSecurityContainerController)
|
||||
whenever(statusBarStateController.isDozing).thenReturn(false)
|
||||
mDependency.injectTestDependency(ShadeController::class.java, shadeController)
|
||||
whenever(dockManager.isDocked).thenReturn(false)
|
||||
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
|
||||
.thenReturn(emptyFlow())
|
||||
|
||||
val featureFlags = FakeFeatureFlags()
|
||||
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
|
||||
featureFlags.set(Flags.DUAL_SHADE, false)
|
||||
val inputProxy = MultiShadeInputProxy()
|
||||
testScope = TestScope()
|
||||
controller =
|
||||
NotificationShadeWindowViewController(
|
||||
lockscreenShadeTransitionController,
|
||||
FalsingCollectorFake(),
|
||||
statusBarStateController,
|
||||
dockManager,
|
||||
notificationShadeDepthController,
|
||||
underTest,
|
||||
notificationPanelViewController,
|
||||
ShadeExpansionStateManager(),
|
||||
notificationStackScrollLayoutController,
|
||||
statusBarKeyguardViewManager,
|
||||
statusBarWindowStateController,
|
||||
lockIconViewController,
|
||||
centralSurfaces,
|
||||
notificationShadeWindowController,
|
||||
keyguardUnlockAnimationController,
|
||||
notificationInsetsController,
|
||||
ambientState,
|
||||
pulsingGestureListener,
|
||||
keyguardBouncerViewModel,
|
||||
keyguardBouncerComponentFactory,
|
||||
alternateBouncerInteractor,
|
||||
udfpsOverlayInteractor,
|
||||
keyguardTransitionInteractor,
|
||||
primaryBouncerToGoneTransitionViewModel,
|
||||
featureFlags,
|
||||
{
|
||||
MultiShadeInteractor(
|
||||
applicationScope = testScope.backgroundScope,
|
||||
repository =
|
||||
MultiShadeRepository(
|
||||
applicationContext = context,
|
||||
inputProxy = inputProxy,
|
||||
),
|
||||
inputProxy = inputProxy,
|
||||
)
|
||||
},
|
||||
FakeSystemClock(),
|
||||
)
|
||||
|
||||
controller.setupExpandedStatusBar()
|
||||
controller.setDragDownHelper(dragDownHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDragDownHelperCalledWhenDraggingDown() =
|
||||
testScope.runTest {
|
||||
whenever(dragDownHelper.isDraggingDown).thenReturn(true)
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
|
||||
underTest.onTouchEvent(ev)
|
||||
verify(dragDownHelper).onTouchEvent(ev)
|
||||
ev.recycle()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInterceptTouchWhenShowingAltAuth() =
|
||||
testScope.runTest {
|
||||
captureInteractionEventHandler()
|
||||
|
||||
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
whenever(statusBarStateController.isDozing).thenReturn(false)
|
||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
||||
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(any())).thenReturn(true)
|
||||
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
|
||||
|
||||
// THEN we should intercept touch
|
||||
assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNoInterceptTouch() =
|
||||
testScope.runTest {
|
||||
captureInteractionEventHandler()
|
||||
|
||||
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
whenever(statusBarStateController.isDozing).thenReturn(false)
|
||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(false)
|
||||
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
|
||||
|
||||
// THEN we shouldn't intercept touch
|
||||
assertThat(interactionEventHandler.shouldInterceptTouchEvent(mock())).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHandleTouchEventWhenShowingAltAuth() =
|
||||
testScope.runTest {
|
||||
captureInteractionEventHandler()
|
||||
|
||||
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
|
||||
whenever(statusBarStateController.isDozing).thenReturn(false)
|
||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
||||
whenever(dragDownHelper.onInterceptTouchEvent(any())).thenReturn(false)
|
||||
|
||||
// THEN we should handle the touch
|
||||
assertThat(interactionEventHandler.handleTouchEvent(mock())).isTrue()
|
||||
}
|
||||
|
||||
private fun captureInteractionEventHandler() {
|
||||
verify(underTest).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
|
||||
interactionEventHandler = interactionEventHandlerCaptor.value
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user