Merge "Multi-shade foundation - integration (5/5)." into udc-dev am: 0003196b1a
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21990414 Change-Id: Id37ddb64fc29c3bc245bc2c9111364d3ff1cb21b 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:clipChildren="false"
|
||||||
android:clipToPadding="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
|
<com.android.systemui.biometrics.AuthRippleView
|
||||||
android:id="@+id/auth_ripple"
|
android:id="@+id/auth_ripple"
|
||||||
android:layout_width="match_parent"
|
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.AudioManager;
|
||||||
import android.media.session.MediaSessionLegacyHelper;
|
import android.media.session.MediaSessionLegacyHelper;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
@@ -31,6 +30,7 @@ import android.view.KeyEvent;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewStub;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.keyguard.AuthKeyguardMessageArea;
|
import com.android.keyguard.AuthKeyguardMessageArea;
|
||||||
@@ -39,8 +39,10 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent;
|
|||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
|
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
|
||||||
import com.android.systemui.classifier.FalsingCollector;
|
import com.android.systemui.classifier.FalsingCollector;
|
||||||
|
import com.android.systemui.compose.ComposeFacade;
|
||||||
import com.android.systemui.dock.DockManager;
|
import com.android.systemui.dock.DockManager;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
|
import com.android.systemui.flags.Flags;
|
||||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||||
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
|
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
|
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.binder.KeyguardBouncerViewBinder;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
|
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.DragDownHelper;
|
||||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||||
import com.android.systemui.statusbar.NotificationInsetsController;
|
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.StatusBarKeyguardViewManager;
|
||||||
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
|
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
|
||||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
|
||||||
|
import com.android.systemui.util.time.SystemClock;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Provider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for {@link NotificationShadeWindowView}.
|
* Controller for {@link NotificationShadeWindowView}.
|
||||||
@@ -115,6 +121,7 @@ public class NotificationShadeWindowViewController {
|
|||||||
mIsOcclusionTransitionRunning =
|
mIsOcclusionTransitionRunning =
|
||||||
step.getTransitionState() == TransitionState.RUNNING;
|
step.getTransitionState() == TransitionState.RUNNING;
|
||||||
};
|
};
|
||||||
|
private final SystemClock mClock;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NotificationShadeWindowViewController(
|
public NotificationShadeWindowViewController(
|
||||||
@@ -142,7 +149,9 @@ public class NotificationShadeWindowViewController {
|
|||||||
UdfpsOverlayInteractor udfpsOverlayInteractor,
|
UdfpsOverlayInteractor udfpsOverlayInteractor,
|
||||||
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
KeyguardTransitionInteractor keyguardTransitionInteractor,
|
||||||
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
|
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
|
||||||
FeatureFlags featureFlags) {
|
FeatureFlags featureFlags,
|
||||||
|
Provider<MultiShadeInteractor> multiShadeInteractorProvider,
|
||||||
|
SystemClock clock) {
|
||||||
mLockscreenShadeTransitionController = transitionController;
|
mLockscreenShadeTransitionController = transitionController;
|
||||||
mFalsingCollector = falsingCollector;
|
mFalsingCollector = falsingCollector;
|
||||||
mStatusBarStateController = statusBarStateController;
|
mStatusBarStateController = statusBarStateController;
|
||||||
@@ -175,6 +184,16 @@ public class NotificationShadeWindowViewController {
|
|||||||
|
|
||||||
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
|
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
|
||||||
mLockscreenToDreamingTransition);
|
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(
|
mLockIconViewController.onTouchEvent(
|
||||||
ev,
|
ev,
|
||||||
() -> mService.wakeUpIfDozing(
|
() -> mService.wakeUpIfDozing(
|
||||||
SystemClock.uptimeMillis(),
|
mClock.uptimeMillis(),
|
||||||
mView,
|
mView,
|
||||||
"LOCK_ICON_TOUCH",
|
"LOCK_ICON_TOUCH",
|
||||||
PowerManager.WAKE_REASON_GESTURE)
|
PowerManager.WAKE_REASON_GESTURE)
|
||||||
@@ -453,7 +472,7 @@ public class NotificationShadeWindowViewController {
|
|||||||
|
|
||||||
public void cancelCurrentTouch() {
|
public void cancelCurrentTouch() {
|
||||||
if (mTouchActive) {
|
if (mTouchActive) {
|
||||||
final long now = SystemClock.uptimeMillis();
|
final long now = mClock.uptimeMillis();
|
||||||
final MotionEvent event;
|
final MotionEvent event;
|
||||||
if (mIsTrackpadGestureBackEnabled) {
|
if (mIsTrackpadGestureBackEnabled) {
|
||||||
event = MotionEvent.obtain(mDownEvent);
|
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.shared.model.TransitionStep
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
|
||||||
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
|
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.shade.NotificationShadeWindowView.InteractionEventHandler
|
||||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
import com.android.systemui.statusbar.LockscreenShadeTransitionController
|
||||||
import com.android.systemui.statusbar.NotificationInsetsController
|
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.phone.StatusBarKeyguardViewManager
|
||||||
import com.android.systemui.statusbar.window.StatusBarWindowStateController
|
import com.android.systemui.statusbar.window.StatusBarWindowStateController
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
@@ -65,10 +72,12 @@ import org.mockito.Mockito.verify
|
|||||||
import org.mockito.Mockito.`when` as whenever
|
import org.mockito.Mockito.`when` as whenever
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(AndroidTestingRunner::class)
|
@RunWith(AndroidTestingRunner::class)
|
||||||
@RunWithLooper(setAsMainLooper = true)
|
@RunWithLooper(setAsMainLooper = true)
|
||||||
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
||||||
|
|
||||||
@Mock private lateinit var view: NotificationShadeWindowView
|
@Mock private lateinit var view: NotificationShadeWindowView
|
||||||
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
|
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
|
||||||
@Mock private lateinit var centralSurfaces: CentralSurfaces
|
@Mock private lateinit var centralSurfaces: CentralSurfaces
|
||||||
@@ -102,6 +111,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
private lateinit var underTest: NotificationShadeWindowViewController
|
private lateinit var underTest: NotificationShadeWindowViewController
|
||||||
|
|
||||||
|
private lateinit var testScope: TestScope
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
@@ -115,8 +126,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
|||||||
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
|
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
|
||||||
.thenReturn(emptyFlow<TransitionStep>())
|
.thenReturn(emptyFlow<TransitionStep>())
|
||||||
|
|
||||||
val featureFlags = FakeFeatureFlags();
|
val featureFlags = FakeFeatureFlags()
|
||||||
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
|
featureFlags.set(Flags.TRACKPAD_GESTURE_BACK, false)
|
||||||
|
featureFlags.set(Flags.DUAL_SHADE, false)
|
||||||
|
|
||||||
|
val inputProxy = MultiShadeInputProxy()
|
||||||
|
testScope = TestScope()
|
||||||
underTest =
|
underTest =
|
||||||
NotificationShadeWindowViewController(
|
NotificationShadeWindowViewController(
|
||||||
lockscreenShadeTransitionController,
|
lockscreenShadeTransitionController,
|
||||||
@@ -144,6 +159,18 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
|||||||
keyguardTransitionInteractor,
|
keyguardTransitionInteractor,
|
||||||
primaryBouncerToGoneTransitionViewModel,
|
primaryBouncerToGoneTransitionViewModel,
|
||||||
featureFlags,
|
featureFlags,
|
||||||
|
{
|
||||||
|
MultiShadeInteractor(
|
||||||
|
applicationScope = testScope.backgroundScope,
|
||||||
|
repository =
|
||||||
|
MultiShadeRepository(
|
||||||
|
applicationContext = context,
|
||||||
|
inputProxy = inputProxy,
|
||||||
|
),
|
||||||
|
inputProxy = inputProxy,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
FakeSystemClock(),
|
||||||
)
|
)
|
||||||
underTest.setupExpandedStatusBar()
|
underTest.setupExpandedStatusBar()
|
||||||
|
|
||||||
@@ -156,147 +183,162 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
|
|||||||
// tests need to be added to test the rest of handleDispatchTouchEvent.
|
// tests need to be added to test the rest of handleDispatchTouchEvent.
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() {
|
fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() =
|
||||||
underTest.setStatusBarViewController(null)
|
testScope.runTest {
|
||||||
|
underTest.setStatusBarViewController(null)
|
||||||
|
|
||||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||||
|
|
||||||
assertThat(returnVal).isFalse()
|
assertThat(returnVal).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() {
|
fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true)
|
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)
|
verify(phoneStatusBarViewController).sendTouchToView(ev)
|
||||||
assertThat(returnVal).isTrue()
|
assertThat(returnVal).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
|
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
val downEvBelow =
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
|
val downEvBelow =
|
||||||
interactionEventHandler.handleDispatchTouchEvent(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)
|
val nextEvent =
|
||||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
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)
|
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||||
assertThat(returnVal).isTrue()
|
assertThat(returnVal).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() {
|
fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||||
.thenReturn(true)
|
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||||
whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true)
|
.thenReturn(true)
|
||||||
|
whenever(phoneStatusBarViewController.sendTouchToView(DOWN_EVENT)).thenReturn(true)
|
||||||
|
|
||||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||||
|
|
||||||
verify(phoneStatusBarViewController).sendTouchToView(downEv)
|
verify(phoneStatusBarViewController).sendTouchToView(DOWN_EVENT)
|
||||||
assertThat(returnVal).isTrue()
|
assertThat(returnVal).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() {
|
fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||||
.thenReturn(true)
|
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||||
// Item we're testing
|
.thenReturn(true)
|
||||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false)
|
// 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)
|
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||||
assertThat(returnVal).isNull()
|
assertThat(returnVal).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() {
|
fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||||
// Item we're testing
|
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
// Item we're testing
|
||||||
.thenReturn(false)
|
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||||
|
.thenReturn(false)
|
||||||
|
|
||||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv)
|
val returnVal = interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||||
|
|
||||||
verify(phoneStatusBarViewController, never()).sendTouchToView(downEv)
|
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||||
assertThat(returnVal).isNull()
|
assertThat(returnVal).isNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() {
|
fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||||
.thenReturn(true)
|
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||||
// Item we're testing
|
.thenReturn(true)
|
||||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false)
|
// 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)
|
verify(phoneStatusBarViewController, never()).sendTouchToView(DOWN_EVENT)
|
||||||
assertThat(returnVal).isTrue()
|
assertThat(returnVal).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() {
|
fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() =
|
||||||
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
testScope.runTest {
|
||||||
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
underTest.setStatusBarViewController(phoneStatusBarViewController)
|
||||||
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true)
|
||||||
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true)
|
||||||
.thenReturn(true)
|
whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat()))
|
||||||
|
.thenReturn(true)
|
||||||
|
|
||||||
// Down event first
|
// Down event first
|
||||||
interactionEventHandler.handleDispatchTouchEvent(downEv)
|
interactionEventHandler.handleDispatchTouchEvent(DOWN_EVENT)
|
||||||
|
|
||||||
// Then another event
|
// Then another event
|
||||||
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
|
val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0)
|
||||||
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
|
||||||
|
|
||||||
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)
|
||||||
|
|
||||||
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
verify(phoneStatusBarViewController).sendTouchToView(nextEvent)
|
||||||
assertThat(returnVal).isTrue()
|
assertThat(returnVal).isTrue()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() {
|
fun shouldInterceptTouchEvent_downEventAlternateBouncer_ignoreIfInUdfpsOverlay() =
|
||||||
// Down event within udfpsOverlay bounds while alternateBouncer is showing
|
testScope.runTest {
|
||||||
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(downEv)).thenReturn(false)
|
// Down event within udfpsOverlay bounds while alternateBouncer is showing
|
||||||
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
whenever(udfpsOverlayInteractor.canInterceptTouchInUdfpsBounds(DOWN_EVENT))
|
||||||
|
.thenReturn(false)
|
||||||
|
whenever(alternateBouncerInteractor.isVisibleState()).thenReturn(true)
|
||||||
|
|
||||||
// Then touch should not be intercepted
|
// Then touch should not be intercepted
|
||||||
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(downEv)
|
val shouldIntercept = interactionEventHandler.shouldInterceptTouchEvent(DOWN_EVENT)
|
||||||
assertThat(shouldIntercept).isFalse()
|
assertThat(shouldIntercept).isFalse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetBouncerContainer() {
|
fun testGetBouncerContainer() =
|
||||||
Mockito.clearInvocations(view)
|
testScope.runTest {
|
||||||
underTest.bouncerContainer
|
Mockito.clearInvocations(view)
|
||||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
|
underTest.bouncerContainer
|
||||||
}
|
verify(view).findViewById<ViewGroup>(R.id.keyguard_bouncer_container)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testGetKeyguardMessageArea() {
|
fun testGetKeyguardMessageArea() =
|
||||||
underTest.keyguardMessageArea
|
testScope.runTest {
|
||||||
verify(view).findViewById<ViewGroup>(R.id.keyguard_message_area)
|
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