Merge changes from topic "caitlinshk-media-ttt-displayview" into tm-qpr-dev am: dd38a02c93
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21024666 Change-Id: I2d1c512fecdee4e0f86be11958f3598aab2d3bd6 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -345,6 +345,10 @@ object Flags {
|
|||||||
// TODO(b/263512203): Tracking Bug
|
// TODO(b/263512203): Tracking Bug
|
||||||
val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
|
val MEDIA_EXPLICIT_INDICATOR = unreleasedFlag(911, "media_explicit_indicator", teamfood = true)
|
||||||
|
|
||||||
|
// TODO(b/265813373): Tracking Bug
|
||||||
|
val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE =
|
||||||
|
unreleasedFlag(912, "media_ttt_dismiss_gesture", teamfood = true)
|
||||||
|
|
||||||
// 1000 - dock
|
// 1000 - dock
|
||||||
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
|
val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging")
|
||||||
|
|
||||||
|
|||||||
@@ -30,4 +30,8 @@ class MediaTttFlags @Inject constructor(private val featureFlags: FeatureFlags)
|
|||||||
/** Check whether the flag for the receiver success state is enabled. */
|
/** Check whether the flag for the receiver success state is enabled. */
|
||||||
fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
|
fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
|
||||||
featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
|
featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
|
||||||
|
|
||||||
|
/** True if the media transfer chip can be dismissed via a gesture. */
|
||||||
|
fun isMediaTttDismissGestureEnabled(): Boolean =
|
||||||
|
featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ constructor(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
vibrationEffect = chipStateSender.transferStatus.vibrationEffect,
|
vibrationEffect = chipStateSender.transferStatus.vibrationEffect,
|
||||||
|
allowSwipeToDismiss = true,
|
||||||
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
|
windowTitle = MediaTttUtils.WINDOW_TITLE_SENDER,
|
||||||
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
|
wakeReason = MediaTttUtils.WAKE_REASON_SENDER,
|
||||||
timeoutMs = chipStateSender.timeout,
|
timeoutMs = chipStateSender.timeout,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
|
|||||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
|
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
|
||||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
|
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
|
||||||
import androidx.annotation.CallSuper
|
import androidx.annotation.CallSuper
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import com.android.systemui.CoreStartable
|
import com.android.systemui.CoreStartable
|
||||||
import com.android.systemui.Dumpable
|
import com.android.systemui.Dumpable
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
@@ -108,9 +109,10 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
* Whenever the current view disappears, the next-priority view will be displayed if it's still
|
* Whenever the current view disappears, the next-priority view will be displayed if it's still
|
||||||
* valid.
|
* valid.
|
||||||
*/
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
internal val activeViews: MutableList<DisplayInfo> = mutableListOf()
|
internal val activeViews: MutableList<DisplayInfo> = mutableListOf()
|
||||||
|
|
||||||
private fun getCurrentDisplayInfo(): DisplayInfo? {
|
internal fun getCurrentDisplayInfo(): DisplayInfo? {
|
||||||
return activeViews.getOrNull(0)
|
return activeViews.getOrNull(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ constructor(
|
|||||||
powerManager: PowerManager,
|
powerManager: PowerManager,
|
||||||
private val falsingManager: FalsingManager,
|
private val falsingManager: FalsingManager,
|
||||||
private val falsingCollector: FalsingCollector,
|
private val falsingCollector: FalsingCollector,
|
||||||
|
private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler?,
|
||||||
private val viewUtil: ViewUtil,
|
private val viewUtil: ViewUtil,
|
||||||
private val vibratorHelper: VibratorHelper,
|
private val vibratorHelper: VibratorHelper,
|
||||||
wakeLockBuilder: WakeLock.Builder,
|
wakeLockBuilder: WakeLock.Builder,
|
||||||
@@ -105,6 +106,8 @@ constructor(
|
|||||||
commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }
|
commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }
|
||||||
|
|
||||||
override fun updateView(newInfo: ChipbarInfo, currentView: ViewGroup) {
|
override fun updateView(newInfo: ChipbarInfo, currentView: ViewGroup) {
|
||||||
|
updateGestureListening()
|
||||||
|
|
||||||
logger.logViewUpdate(
|
logger.logViewUpdate(
|
||||||
newInfo.windowTitle,
|
newInfo.windowTitle,
|
||||||
newInfo.text.loadText(context),
|
newInfo.text.loadText(context),
|
||||||
@@ -228,6 +231,42 @@ constructor(
|
|||||||
includeMargins = true,
|
includeMargins = true,
|
||||||
onAnimationEnd,
|
onAnimationEnd,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
updateGestureListening()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateGestureListening() {
|
||||||
|
if (swipeChipbarAwayGestureHandler == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val currentDisplayInfo = getCurrentDisplayInfo()
|
||||||
|
if (currentDisplayInfo != null && currentDisplayInfo.info.allowSwipeToDismiss) {
|
||||||
|
swipeChipbarAwayGestureHandler.setViewFetcher { currentDisplayInfo.view }
|
||||||
|
swipeChipbarAwayGestureHandler.addOnGestureDetectedCallback(TAG) {
|
||||||
|
onSwipeUpGestureDetected()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
swipeChipbarAwayGestureHandler.resetViewFetcher()
|
||||||
|
swipeChipbarAwayGestureHandler.removeOnGestureDetectedCallback(TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onSwipeUpGestureDetected() {
|
||||||
|
val currentDisplayInfo = getCurrentDisplayInfo()
|
||||||
|
if (currentDisplayInfo == null) {
|
||||||
|
logger.logSwipeGestureError(id = null, errorMsg = "No info is being displayed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!currentDisplayInfo.info.allowSwipeToDismiss) {
|
||||||
|
logger.logSwipeGestureError(
|
||||||
|
id = currentDisplayInfo.info.id,
|
||||||
|
errorMsg = "This view prohibits swipe-to-dismiss",
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
removeView(currentDisplayInfo.info.id, SWIPE_UP_GESTURE_REASON)
|
||||||
|
updateGestureListening()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ViewGroup.getInnerView(): ViewGroup {
|
private fun ViewGroup.getInnerView(): ViewGroup {
|
||||||
@@ -250,3 +289,5 @@ constructor(
|
|||||||
private const val ANIMATION_IN_DURATION = 500L
|
private const val ANIMATION_IN_DURATION = 500L
|
||||||
private const val ANIMATION_OUT_DURATION = 250L
|
private const val ANIMATION_OUT_DURATION = 250L
|
||||||
@IdRes private val INFO_TAG = R.id.tag_chipbar_info
|
@IdRes private val INFO_TAG = R.id.tag_chipbar_info
|
||||||
|
private const val SWIPE_UP_GESTURE_REASON = "SWIPE_UP_GESTURE_DETECTED"
|
||||||
|
private const val TAG = "ChipbarCoordinator"
|
||||||
|
|||||||
@@ -33,12 +33,14 @@ import com.android.systemui.temporarydisplay.ViewPriority
|
|||||||
* @property endItem an optional end item to display at the end of the chipbar (on the right in LTR
|
* @property endItem an optional end item to display at the end of the chipbar (on the right in LTR
|
||||||
* locales; on the left in RTL locales).
|
* locales; on the left in RTL locales).
|
||||||
* @property vibrationEffect an optional vibration effect when the chipbar is displayed
|
* @property vibrationEffect an optional vibration effect when the chipbar is displayed
|
||||||
|
* @property allowSwipeToDismiss true if users are allowed to swipe up to dismiss this chipbar.
|
||||||
*/
|
*/
|
||||||
data class ChipbarInfo(
|
data class ChipbarInfo(
|
||||||
val startIcon: TintedIcon,
|
val startIcon: TintedIcon,
|
||||||
val text: Text,
|
val text: Text,
|
||||||
val endItem: ChipbarEndItem?,
|
val endItem: ChipbarEndItem?,
|
||||||
val vibrationEffect: VibrationEffect? = null,
|
val vibrationEffect: VibrationEffect? = null,
|
||||||
|
val allowSwipeToDismiss: Boolean = false,
|
||||||
override val windowTitle: String,
|
override val windowTitle: String,
|
||||||
override val wakeReason: String,
|
override val wakeReason: String,
|
||||||
override val timeoutMs: Int,
|
override val timeoutMs: Int,
|
||||||
|
|||||||
@@ -46,4 +46,16 @@ constructor(
|
|||||||
{ "Chipbar updated. window=$str1 text=$str2 endItem=$str3" }
|
{ "Chipbar updated. window=$str1 text=$str2 endItem=$str3" }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun logSwipeGestureError(id: String?, errorMsg: String) {
|
||||||
|
buffer.log(
|
||||||
|
tag,
|
||||||
|
LogLevel.WARNING,
|
||||||
|
{
|
||||||
|
str1 = id
|
||||||
|
str2 = errorMsg
|
||||||
|
},
|
||||||
|
{ "Chipbar swipe gesture detected for incorrect state. id=$str1 error=$str2" }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.temporarydisplay.chipbar
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
|
||||||
|
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
|
||||||
|
import com.android.systemui.util.boundsOnScreen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to detect when a user has swiped the chipbar away.
|
||||||
|
*
|
||||||
|
* Effectively [SysUISingleton]. But, this shouldn't be created if the gesture isn't enabled. See
|
||||||
|
* [TemporaryDisplayModule.provideSwipeChipbarAwayGestureHandler].
|
||||||
|
*/
|
||||||
|
class SwipeChipbarAwayGestureHandler(
|
||||||
|
context: Context,
|
||||||
|
logger: SwipeUpGestureLogger,
|
||||||
|
) : SwipeUpGestureHandler(context, logger, loggerTag = LOGGER_TAG) {
|
||||||
|
|
||||||
|
private var viewFetcher: () -> View? = { null }
|
||||||
|
|
||||||
|
override fun startOfGestureIsWithinBounds(ev: MotionEvent): Boolean {
|
||||||
|
val view = viewFetcher.invoke() ?: return false
|
||||||
|
// Since chipbar is in its own window, we need to use [boundsOnScreen] to get an accurate
|
||||||
|
// bottom. ([view.bottom] would be relative to its window, which would be too small.)
|
||||||
|
val viewBottom = view.boundsOnScreen.bottom
|
||||||
|
// Allow the gesture to start a bit below the chipbar
|
||||||
|
return ev.y <= 1.5 * viewBottom
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a fetcher that returns the current chipbar view. The fetcher will be invoked whenever a
|
||||||
|
* gesture starts to determine if the gesture is near the chipbar.
|
||||||
|
*/
|
||||||
|
fun setViewFetcher(fetcher: () -> View?) {
|
||||||
|
viewFetcher = fetcher
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes the current view fetcher. */
|
||||||
|
fun resetViewFetcher() {
|
||||||
|
viewFetcher = { null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val LOGGER_TAG = "SwipeChipbarAway"
|
||||||
@@ -16,22 +16,38 @@
|
|||||||
|
|
||||||
package com.android.systemui.temporarydisplay.dagger
|
package com.android.systemui.temporarydisplay.dagger
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.log.LogBufferFactory
|
import com.android.systemui.log.LogBufferFactory
|
||||||
|
import com.android.systemui.media.taptotransfer.MediaTttFlags
|
||||||
import com.android.systemui.plugins.log.LogBuffer
|
import com.android.systemui.plugins.log.LogBuffer
|
||||||
|
import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
|
||||||
|
import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
interface TemporaryDisplayModule {
|
interface TemporaryDisplayModule {
|
||||||
@Module
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
|
||||||
@Provides
|
@Provides
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@ChipbarLog
|
@ChipbarLog
|
||||||
fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
|
fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
|
||||||
return factory.create("ChipbarLog", 40)
|
return factory.create("ChipbarLog", 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@SysUISingleton
|
||||||
|
fun provideSwipeChipbarAwayGestureHandler(
|
||||||
|
mediaTttFlags: MediaTttFlags,
|
||||||
|
context: Context,
|
||||||
|
logger: SwipeUpGestureLogger,
|
||||||
|
): SwipeChipbarAwayGestureHandler? {
|
||||||
|
return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
|
||||||
|
SwipeChipbarAwayGestureHandler(context, logger)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
|
|||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
|
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
|
||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
|
import com.android.systemui.temporarydisplay.chipbar.ChipbarLogger
|
||||||
import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
|
import com.android.systemui.temporarydisplay.chipbar.FakeChipbarCoordinator
|
||||||
|
import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.argumentCaptor
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
@@ -98,6 +99,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var viewUtil: ViewUtil
|
@Mock private lateinit var viewUtil: ViewUtil
|
||||||
@Mock private lateinit var windowManager: WindowManager
|
@Mock private lateinit var windowManager: WindowManager
|
||||||
@Mock private lateinit var vibratorHelper: VibratorHelper
|
@Mock private lateinit var vibratorHelper: VibratorHelper
|
||||||
|
@Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
|
||||||
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
|
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
|
||||||
private lateinit var fakeWakeLock: WakeLockFake
|
private lateinit var fakeWakeLock: WakeLockFake
|
||||||
private lateinit var chipbarCoordinator: ChipbarCoordinator
|
private lateinit var chipbarCoordinator: ChipbarCoordinator
|
||||||
@@ -148,6 +150,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
powerManager,
|
powerManager,
|
||||||
falsingManager,
|
falsingManager,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
|
swipeHandler,
|
||||||
viewUtil,
|
viewUtil,
|
||||||
vibratorHelper,
|
vibratorHelper,
|
||||||
fakeWakeLockBuilder,
|
fakeWakeLockBuilder,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.os.PowerManager
|
|||||||
import android.os.VibrationEffect
|
import android.os.VibrationEffect
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.testing.TestableLooper
|
import android.testing.TestableLooper
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
@@ -43,6 +44,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController
|
|||||||
import com.android.systemui.temporarydisplay.ViewPriority
|
import com.android.systemui.temporarydisplay.ViewPriority
|
||||||
import com.android.systemui.util.concurrency.FakeExecutor
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.android.systemui.util.mockito.capture
|
||||||
import com.android.systemui.util.mockito.eq
|
import com.android.systemui.util.mockito.eq
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import com.android.systemui.util.view.ViewUtil
|
import com.android.systemui.util.view.ViewUtil
|
||||||
@@ -54,6 +57,7 @@ import org.junit.runner.RunWith
|
|||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
import org.mockito.ArgumentMatchers.anyInt
|
import org.mockito.ArgumentMatchers.anyInt
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.verify
|
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
|
||||||
@@ -74,6 +78,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var falsingCollector: FalsingCollector
|
@Mock private lateinit var falsingCollector: FalsingCollector
|
||||||
@Mock private lateinit var viewUtil: ViewUtil
|
@Mock private lateinit var viewUtil: ViewUtil
|
||||||
@Mock private lateinit var vibratorHelper: VibratorHelper
|
@Mock private lateinit var vibratorHelper: VibratorHelper
|
||||||
|
@Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
|
||||||
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
|
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
|
||||||
private lateinit var fakeWakeLock: WakeLockFake
|
private lateinit var fakeWakeLock: WakeLockFake
|
||||||
private lateinit var fakeClock: FakeSystemClock
|
private lateinit var fakeClock: FakeSystemClock
|
||||||
@@ -106,6 +111,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
|
|||||||
powerManager,
|
powerManager,
|
||||||
falsingManager,
|
falsingManager,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
|
swipeGestureHandler,
|
||||||
viewUtil,
|
viewUtil,
|
||||||
vibratorHelper,
|
vibratorHelper,
|
||||||
fakeWakeLockBuilder,
|
fakeWakeLockBuilder,
|
||||||
@@ -430,17 +436,101 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
|
verify(logger).logViewUpdate(eq(WINDOW_TITLE), eq("new title text"), any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun swipeToDismiss_false_neverListensForGesture() {
|
||||||
|
underTest.displayView(
|
||||||
|
createChipbarInfo(
|
||||||
|
Icon.Resource(R.drawable.ic_cake, contentDescription = null),
|
||||||
|
Text.Loaded("title text"),
|
||||||
|
endItem = ChipbarEndItem.Loading,
|
||||||
|
allowSwipeToDismiss = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(swipeGestureHandler, never()).addOnGestureDetectedCallback(any(), any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun swipeToDismiss_true_listensForGesture() {
|
||||||
|
underTest.displayView(
|
||||||
|
createChipbarInfo(
|
||||||
|
Icon.Resource(R.drawable.ic_cake, contentDescription = null),
|
||||||
|
Text.Loaded("title text"),
|
||||||
|
endItem = ChipbarEndItem.Loading,
|
||||||
|
allowSwipeToDismiss = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun swipeToDismiss_swipeOccurs_viewDismissed() {
|
||||||
|
underTest.displayView(
|
||||||
|
createChipbarInfo(
|
||||||
|
Icon.Resource(R.drawable.ic_cake, contentDescription = null),
|
||||||
|
Text.Loaded("title text"),
|
||||||
|
endItem = ChipbarEndItem.Loading,
|
||||||
|
allowSwipeToDismiss = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val view = getChipbarView()
|
||||||
|
|
||||||
|
val callbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
|
||||||
|
verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), capture(callbackCaptor))
|
||||||
|
|
||||||
|
callbackCaptor.value.invoke(MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0))
|
||||||
|
|
||||||
|
verify(windowManager).removeView(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun swipeToDismiss_viewUpdatedToFalse_swipeOccurs_viewNotDismissed() {
|
||||||
|
underTest.displayView(
|
||||||
|
createChipbarInfo(
|
||||||
|
Icon.Resource(R.drawable.ic_cake, contentDescription = null),
|
||||||
|
Text.Loaded("title text"),
|
||||||
|
endItem = ChipbarEndItem.Loading,
|
||||||
|
allowSwipeToDismiss = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val view = getChipbarView()
|
||||||
|
val callbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
|
||||||
|
verify(swipeGestureHandler).addOnGestureDetectedCallback(any(), capture(callbackCaptor))
|
||||||
|
|
||||||
|
// WHEN the view is updated to not allow swipe-to-dismiss
|
||||||
|
underTest.displayView(
|
||||||
|
createChipbarInfo(
|
||||||
|
Icon.Resource(R.drawable.ic_cake, contentDescription = null),
|
||||||
|
Text.Loaded("title text"),
|
||||||
|
endItem = ChipbarEndItem.Loading,
|
||||||
|
allowSwipeToDismiss = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN the callback is removed
|
||||||
|
verify(swipeGestureHandler).removeOnGestureDetectedCallback(any())
|
||||||
|
|
||||||
|
// And WHEN the old callback is invoked
|
||||||
|
callbackCaptor.value.invoke(MotionEvent.obtain(0L, 0L, 0, 0f, 0f, 0))
|
||||||
|
|
||||||
|
// THEN it is ignored and view isn't removed
|
||||||
|
verify(windowManager, never()).removeView(view)
|
||||||
|
}
|
||||||
|
|
||||||
private fun createChipbarInfo(
|
private fun createChipbarInfo(
|
||||||
startIcon: Icon,
|
startIcon: Icon,
|
||||||
text: Text,
|
text: Text,
|
||||||
endItem: ChipbarEndItem?,
|
endItem: ChipbarEndItem?,
|
||||||
vibrationEffect: VibrationEffect? = null,
|
vibrationEffect: VibrationEffect? = null,
|
||||||
|
allowSwipeToDismiss: Boolean = false,
|
||||||
): ChipbarInfo {
|
): ChipbarInfo {
|
||||||
return ChipbarInfo(
|
return ChipbarInfo(
|
||||||
TintedIcon(startIcon, tintAttr = null),
|
TintedIcon(startIcon, tintAttr = null),
|
||||||
text,
|
text,
|
||||||
endItem,
|
endItem,
|
||||||
vibrationEffect,
|
vibrationEffect,
|
||||||
|
allowSwipeToDismiss,
|
||||||
windowTitle = WINDOW_TITLE,
|
windowTitle = WINDOW_TITLE,
|
||||||
wakeReason = WAKE_REASON,
|
wakeReason = WAKE_REASON,
|
||||||
timeoutMs = TIMEOUT,
|
timeoutMs = TIMEOUT,
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class FakeChipbarCoordinator(
|
|||||||
powerManager: PowerManager,
|
powerManager: PowerManager,
|
||||||
falsingManager: FalsingManager,
|
falsingManager: FalsingManager,
|
||||||
falsingCollector: FalsingCollector,
|
falsingCollector: FalsingCollector,
|
||||||
|
swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
|
||||||
viewUtil: ViewUtil,
|
viewUtil: ViewUtil,
|
||||||
vibratorHelper: VibratorHelper,
|
vibratorHelper: VibratorHelper,
|
||||||
wakeLockBuilder: WakeLock.Builder,
|
wakeLockBuilder: WakeLock.Builder,
|
||||||
@@ -59,6 +60,7 @@ class FakeChipbarCoordinator(
|
|||||||
powerManager,
|
powerManager,
|
||||||
falsingManager,
|
falsingManager,
|
||||||
falsingCollector,
|
falsingCollector,
|
||||||
|
swipeChipbarAwayGestureHandler,
|
||||||
viewUtil,
|
viewUtil,
|
||||||
vibratorHelper,
|
vibratorHelper,
|
||||||
wakeLockBuilder,
|
wakeLockBuilder,
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* 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.temporarydisplay.chipbar
|
||||||
|
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.mock
|
||||||
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
class SwipeChipbarAwayGestureHandlerTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var underTest: SwipeChipbarAwayGestureHandler
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
underTest = SwipeChipbarAwayGestureHandler(context, mock())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startOfGestureIsWithinBounds_noViewFetcher_returnsFalse() {
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(createMotionEvent())).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startOfGestureIsWithinBounds_usesViewFetcher_aboveBottom_returnsTrue() {
|
||||||
|
val view = createMockView()
|
||||||
|
|
||||||
|
underTest.setViewFetcher { view }
|
||||||
|
|
||||||
|
val motionEvent = createMotionEvent(y = VIEW_BOTTOM - 100f)
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startOfGestureIsWithinBounds_usesViewFetcher_slightlyBelowBottom_returnsTrue() {
|
||||||
|
val view = createMockView()
|
||||||
|
|
||||||
|
underTest.setViewFetcher { view }
|
||||||
|
|
||||||
|
val motionEvent = createMotionEvent(y = VIEW_BOTTOM + 20f)
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startOfGestureIsWithinBounds_usesViewFetcher_tooFarDown_returnsFalse() {
|
||||||
|
val view = createMockView()
|
||||||
|
|
||||||
|
underTest.setViewFetcher { view }
|
||||||
|
|
||||||
|
val motionEvent = createMotionEvent(y = VIEW_BOTTOM * 4f)
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun startOfGestureIsWithinBounds_viewFetcherReset_returnsFalse() {
|
||||||
|
val view = createMockView()
|
||||||
|
|
||||||
|
underTest.setViewFetcher { view }
|
||||||
|
|
||||||
|
val motionEvent = createMotionEvent(y = VIEW_BOTTOM - 100f)
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isTrue()
|
||||||
|
|
||||||
|
underTest.resetViewFetcher()
|
||||||
|
assertThat(underTest.startOfGestureIsWithinBounds(motionEvent)).isFalse()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMotionEvent(y: Float = 0f): MotionEvent {
|
||||||
|
return MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0f, y, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createMockView(): View {
|
||||||
|
return mock<View>().also {
|
||||||
|
doAnswer { invocation ->
|
||||||
|
val out: Rect = invocation.getArgument(0)
|
||||||
|
out.set(0, 0, 0, VIEW_BOTTOM)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
.whenever(it)
|
||||||
|
.getBoundsOnScreen(any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val VIEW_BOTTOM = 455
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user