[Media TTT] Use a listener pattern to notify about view removals. am: 5a7e9e6349
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21020063 Change-Id: Ib292e14c52e26fbf5eb676ecd9f6ac5300b65b26 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -30,6 +30,7 @@ import com.android.systemui.media.taptotransfer.MediaTttFlags
|
|||||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
||||||
import com.android.systemui.statusbar.CommandQueue
|
import com.android.systemui.statusbar.CommandQueue
|
||||||
|
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||||
import com.android.systemui.temporarydisplay.ViewPriority
|
import com.android.systemui.temporarydisplay.ViewPriority
|
||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
|
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
|
||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
|
import com.android.systemui.temporarydisplay.chipbar.ChipbarEndItem
|
||||||
@@ -54,6 +55,7 @@ constructor(
|
|||||||
|
|
||||||
private var displayedState: ChipStateSender? = null
|
private var displayedState: ChipStateSender? = null
|
||||||
// A map to store current chip state per id.
|
// A map to store current chip state per id.
|
||||||
|
// TODO(b/265455911): Log whenever we add or remove from the store.
|
||||||
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
|
private var stateMap: MutableMap<String, ChipStateSender> = mutableMapOf()
|
||||||
|
|
||||||
private val commandQueueCallbacks =
|
private val commandQueueCallbacks =
|
||||||
@@ -102,10 +104,9 @@ constructor(
|
|||||||
}
|
}
|
||||||
uiEventLogger.logSenderStateChange(chipState)
|
uiEventLogger.logSenderStateChange(chipState)
|
||||||
|
|
||||||
stateMap.put(routeInfo.id, chipState)
|
|
||||||
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
|
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
|
||||||
// No need to store the state since it is the default state
|
// No need to store the state since it is the default state
|
||||||
stateMap.remove(routeInfo.id)
|
removeIdFromStore(routeInfo.id)
|
||||||
// Return early if we're not displaying a chip anyway
|
// Return early if we're not displaying a chip anyway
|
||||||
val currentDisplayedState = displayedState ?: return
|
val currentDisplayedState = displayedState ?: return
|
||||||
|
|
||||||
@@ -126,7 +127,9 @@ constructor(
|
|||||||
displayedState = null
|
displayedState = null
|
||||||
chipbarCoordinator.removeView(routeInfo.id, removalReason)
|
chipbarCoordinator.removeView(routeInfo.id, removalReason)
|
||||||
} else {
|
} else {
|
||||||
|
stateMap[routeInfo.id] = chipState
|
||||||
displayedState = chipState
|
displayedState = chipState
|
||||||
|
chipbarCoordinator.registerListener(displayListener)
|
||||||
chipbarCoordinator.displayView(
|
chipbarCoordinator.displayView(
|
||||||
createChipbarInfo(
|
createChipbarInfo(
|
||||||
chipState,
|
chipState,
|
||||||
@@ -135,7 +138,7 @@ constructor(
|
|||||||
context,
|
context,
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
) { stateMap.remove(routeInfo.id) }
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,4 +228,14 @@ constructor(
|
|||||||
onClickListener,
|
onClickListener,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val displayListener =
|
||||||
|
TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
|
||||||
|
|
||||||
|
private fun removeIdFromStore(id: String) {
|
||||||
|
stateMap.remove(id)
|
||||||
|
if (stateMap.isEmpty()) {
|
||||||
|
chipbarCoordinator.unregisterListener(displayListener)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,15 +119,26 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
dumpManager.registerNormalDumpable(this)
|
dumpManager.registerNormalDumpable(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val listeners: MutableSet<Listener> = mutableSetOf()
|
||||||
|
|
||||||
|
/** Registers a listener. */
|
||||||
|
fun registerListener(listener: Listener) {
|
||||||
|
listeners.add(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unregisters a listener. */
|
||||||
|
fun unregisterListener(listener: Listener) {
|
||||||
|
listeners.remove(listener)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the view with the provided [newInfo].
|
* Displays the view with the provided [newInfo].
|
||||||
*
|
*
|
||||||
* This method handles inflating and attaching the view, then delegates to [updateView] to
|
* This method handles inflating and attaching the view, then delegates to [updateView] to
|
||||||
* display the correct information in the view.
|
* display the correct information in the view.
|
||||||
* @param onViewTimeout a runnable that runs after the view timeout.
|
|
||||||
*/
|
*/
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun displayView(newInfo: T, onViewTimeout: Runnable? = null) {
|
fun displayView(newInfo: T) {
|
||||||
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
|
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
|
||||||
newInfo.timeoutMs,
|
newInfo.timeoutMs,
|
||||||
// Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
|
// Not all views have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
|
||||||
@@ -146,14 +157,13 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
logger.logViewUpdate(newInfo)
|
logger.logViewUpdate(newInfo)
|
||||||
currentDisplayInfo.info = newInfo
|
currentDisplayInfo.info = newInfo
|
||||||
currentDisplayInfo.timeExpirationMillis = timeExpirationMillis
|
currentDisplayInfo.timeExpirationMillis = timeExpirationMillis
|
||||||
updateTimeout(currentDisplayInfo, timeout, onViewTimeout)
|
updateTimeout(currentDisplayInfo, timeout)
|
||||||
updateView(newInfo, view)
|
updateView(newInfo, view)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val newDisplayInfo = DisplayInfo(
|
val newDisplayInfo = DisplayInfo(
|
||||||
info = newInfo,
|
info = newInfo,
|
||||||
onViewTimeout = onViewTimeout,
|
|
||||||
timeExpirationMillis = timeExpirationMillis,
|
timeExpirationMillis = timeExpirationMillis,
|
||||||
// Null values will be updated to non-null if/when this view actually gets displayed
|
// Null values will be updated to non-null if/when this view actually gets displayed
|
||||||
view = null,
|
view = null,
|
||||||
@@ -196,7 +206,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
private fun showNewView(newDisplayInfo: DisplayInfo, timeout: Int) {
|
private fun showNewView(newDisplayInfo: DisplayInfo, timeout: Int) {
|
||||||
logger.logViewAddition(newDisplayInfo.info)
|
logger.logViewAddition(newDisplayInfo.info)
|
||||||
createAndAcquireWakeLock(newDisplayInfo)
|
createAndAcquireWakeLock(newDisplayInfo)
|
||||||
updateTimeout(newDisplayInfo, timeout, newDisplayInfo.onViewTimeout)
|
updateTimeout(newDisplayInfo, timeout)
|
||||||
inflateAndUpdateView(newDisplayInfo)
|
inflateAndUpdateView(newDisplayInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,19 +237,16 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
/**
|
/**
|
||||||
* Creates a runnable that will remove [displayInfo] in [timeout] ms from now.
|
* Creates a runnable that will remove [displayInfo] in [timeout] ms from now.
|
||||||
*
|
*
|
||||||
* @param onViewTimeout an optional runnable that will be run if the view times out.
|
|
||||||
* @return a runnable that, when run, will *cancel* the view's timeout.
|
* @return a runnable that, when run, will *cancel* the view's timeout.
|
||||||
*/
|
*/
|
||||||
private fun updateTimeout(displayInfo: DisplayInfo, timeout: Int, onViewTimeout: Runnable?) {
|
private fun updateTimeout(displayInfo: DisplayInfo, timeout: Int) {
|
||||||
val cancelViewTimeout = mainExecutor.executeDelayed(
|
val cancelViewTimeout = mainExecutor.executeDelayed(
|
||||||
{
|
{
|
||||||
removeView(displayInfo.info.id, REMOVAL_REASON_TIMEOUT)
|
removeView(displayInfo.info.id, REMOVAL_REASON_TIMEOUT)
|
||||||
onViewTimeout?.run()
|
|
||||||
},
|
},
|
||||||
timeout.toLong()
|
timeout.toLong()
|
||||||
)
|
)
|
||||||
|
|
||||||
displayInfo.onViewTimeout = onViewTimeout
|
|
||||||
// Cancel old view timeout and re-set it.
|
// Cancel old view timeout and re-set it.
|
||||||
displayInfo.cancelViewTimeout?.run()
|
displayInfo.cancelViewTimeout?.run()
|
||||||
displayInfo.cancelViewTimeout = cancelViewTimeout
|
displayInfo.cancelViewTimeout = cancelViewTimeout
|
||||||
@@ -317,6 +324,9 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
// event comes in while this view is animating out, we still display the new view
|
// event comes in while this view is animating out, we still display the new view
|
||||||
// appropriately.
|
// appropriately.
|
||||||
activeViews.remove(displayInfo)
|
activeViews.remove(displayInfo)
|
||||||
|
listeners.forEach {
|
||||||
|
it.onInfoPermanentlyRemoved(id)
|
||||||
|
}
|
||||||
|
|
||||||
// No need to time the view out since it's already gone
|
// No need to time the view out since it's already gone
|
||||||
displayInfo.cancelViewTimeout?.run()
|
displayInfo.cancelViewTimeout?.run()
|
||||||
@@ -380,6 +390,9 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
invalidViews.forEach {
|
invalidViews.forEach {
|
||||||
activeViews.remove(it)
|
activeViews.remove(it)
|
||||||
logger.logViewExpiration(it.info)
|
logger.logViewExpiration(it.info)
|
||||||
|
listeners.forEach { listener ->
|
||||||
|
listener.onInfoPermanentlyRemoved(it.info.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +449,15 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
onAnimationEnd.run()
|
onAnimationEnd.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A listener interface to be notified of various view events. */
|
||||||
|
fun interface Listener {
|
||||||
|
/**
|
||||||
|
* Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
|
||||||
|
* displayed again (unless another call to [updateView] is made).
|
||||||
|
*/
|
||||||
|
fun onInfoPermanentlyRemoved(id: String)
|
||||||
|
}
|
||||||
|
|
||||||
/** A container for all the display-related state objects. */
|
/** A container for all the display-related state objects. */
|
||||||
inner class DisplayInfo(
|
inner class DisplayInfo(
|
||||||
/**
|
/**
|
||||||
@@ -460,11 +482,6 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
*/
|
*/
|
||||||
var wakeLock: WakeLock?,
|
var wakeLock: WakeLock?,
|
||||||
|
|
||||||
/**
|
|
||||||
* See [displayView].
|
|
||||||
*/
|
|
||||||
var onViewTimeout: Runnable?,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A runnable that, when run, will cancel this view's timeout.
|
* A runnable that, when run, will cancel this view's timeout.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -45,13 +45,17 @@ import com.android.systemui.plugins.FalsingManager
|
|||||||
import com.android.systemui.statusbar.CommandQueue
|
import com.android.systemui.statusbar.CommandQueue
|
||||||
import com.android.systemui.statusbar.VibratorHelper
|
import com.android.systemui.statusbar.VibratorHelper
|
||||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||||
|
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
|
||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
|
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.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.mockito.mock
|
||||||
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
|
||||||
import com.android.systemui.util.wakelock.WakeLockFake
|
import com.android.systemui.util.wakelock.WakeLockFake
|
||||||
@@ -61,6 +65,7 @@ import org.junit.Test
|
|||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mockito.ArgumentCaptor
|
import org.mockito.ArgumentCaptor
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.atLeast
|
||||||
import org.mockito.Mockito.never
|
import org.mockito.Mockito.never
|
||||||
import org.mockito.Mockito.reset
|
import org.mockito.Mockito.reset
|
||||||
import org.mockito.Mockito.verify
|
import org.mockito.Mockito.verify
|
||||||
@@ -161,9 +166,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
underTest.start()
|
underTest.start()
|
||||||
|
|
||||||
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
|
setCommandQueueCallback()
|
||||||
verify(commandQueue).addCallback(callbackCaptor.capture())
|
|
||||||
commandQueueCallback = callbackCaptor.value!!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -920,6 +923,172 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun newState_viewListenerRegistered() {
|
||||||
|
val mockChipbarCoordinator = mock<ChipbarCoordinator>()
|
||||||
|
underTest =
|
||||||
|
MediaTttSenderCoordinator(
|
||||||
|
mockChipbarCoordinator,
|
||||||
|
commandQueue,
|
||||||
|
context,
|
||||||
|
logger,
|
||||||
|
mediaTttFlags,
|
||||||
|
uiEventLogger,
|
||||||
|
)
|
||||||
|
underTest.start()
|
||||||
|
// Re-set the command queue callback since we've created a new [MediaTttSenderCoordinator]
|
||||||
|
// with a new callback.
|
||||||
|
setCommandQueueCallback()
|
||||||
|
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
verify(mockChipbarCoordinator).registerListener(any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onInfoPermanentlyRemoved_viewListenerUnregistered() {
|
||||||
|
val mockChipbarCoordinator = mock<ChipbarCoordinator>()
|
||||||
|
underTest =
|
||||||
|
MediaTttSenderCoordinator(
|
||||||
|
mockChipbarCoordinator,
|
||||||
|
commandQueue,
|
||||||
|
context,
|
||||||
|
logger,
|
||||||
|
mediaTttFlags,
|
||||||
|
uiEventLogger,
|
||||||
|
)
|
||||||
|
underTest.start()
|
||||||
|
setCommandQueueCallback()
|
||||||
|
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
|
||||||
|
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
|
// WHEN the listener is notified that the view has been removed
|
||||||
|
listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
|
||||||
|
|
||||||
|
// THEN the media coordinator unregisters the listener
|
||||||
|
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onInfoPermanentlyRemoved_wrongId_viewListenerNotUnregistered() {
|
||||||
|
val mockChipbarCoordinator = mock<ChipbarCoordinator>()
|
||||||
|
underTest =
|
||||||
|
MediaTttSenderCoordinator(
|
||||||
|
mockChipbarCoordinator,
|
||||||
|
commandQueue,
|
||||||
|
context,
|
||||||
|
logger,
|
||||||
|
mediaTttFlags,
|
||||||
|
uiEventLogger,
|
||||||
|
)
|
||||||
|
underTest.start()
|
||||||
|
setCommandQueueCallback()
|
||||||
|
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
|
||||||
|
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
|
// WHEN the listener is notified that a different view has been removed
|
||||||
|
listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
|
||||||
|
|
||||||
|
// THEN the media coordinator doesn't unregister the listener
|
||||||
|
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun farFromReceiverState_viewListenerUnregistered() {
|
||||||
|
val mockChipbarCoordinator = mock<ChipbarCoordinator>()
|
||||||
|
underTest =
|
||||||
|
MediaTttSenderCoordinator(
|
||||||
|
mockChipbarCoordinator,
|
||||||
|
commandQueue,
|
||||||
|
context,
|
||||||
|
logger,
|
||||||
|
mediaTttFlags,
|
||||||
|
uiEventLogger,
|
||||||
|
)
|
||||||
|
underTest.start()
|
||||||
|
setCommandQueueCallback()
|
||||||
|
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
|
||||||
|
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
|
// WHEN we go to the FAR_FROM_RECEIVER state
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||||
|
routeInfo,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN the media coordinator unregisters the listener
|
||||||
|
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun statesWithDifferentIds_onInfoPermanentlyRemovedForOneId_viewListenerNotUnregistered() {
|
||||||
|
val mockChipbarCoordinator = mock<ChipbarCoordinator>()
|
||||||
|
underTest =
|
||||||
|
MediaTttSenderCoordinator(
|
||||||
|
mockChipbarCoordinator,
|
||||||
|
commandQueue,
|
||||||
|
context,
|
||||||
|
logger,
|
||||||
|
mediaTttFlags,
|
||||||
|
uiEventLogger,
|
||||||
|
)
|
||||||
|
underTest.start()
|
||||||
|
setCommandQueueCallback()
|
||||||
|
|
||||||
|
// WHEN there are two different media transfers with different IDs
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
MediaRoute2Info.Builder("route2", OTHER_DEVICE_NAME)
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
val listenerCaptor = argumentCaptor<TemporaryViewDisplayController.Listener>()
|
||||||
|
verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
|
// THEN one of them is removed
|
||||||
|
listenerCaptor.value.onInfoPermanentlyRemoved("route1")
|
||||||
|
|
||||||
|
// THEN the media coordinator doesn't unregister the listener (since route2 is still active)
|
||||||
|
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getChipbarView(): ViewGroup {
|
private fun getChipbarView(): ViewGroup {
|
||||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||||
@@ -960,8 +1129,16 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setCommandQueueCallback() {
|
||||||
|
val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
|
||||||
|
verify(commandQueue).addCallback(capture(callbackCaptor))
|
||||||
|
commandQueueCallback = callbackCaptor.value
|
||||||
|
reset(commandQueue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val DEFAULT_ID = "defaultId"
|
||||||
private const val APP_NAME = "Fake app name"
|
private const val APP_NAME = "Fake app name"
|
||||||
private const val OTHER_DEVICE_NAME = "My Tablet"
|
private const val OTHER_DEVICE_NAME = "My Tablet"
|
||||||
private const val BLANK_DEVICE_NAME = " "
|
private const val BLANK_DEVICE_NAME = " "
|
||||||
@@ -969,13 +1146,13 @@ private const val PACKAGE_NAME = "com.android.systemui"
|
|||||||
private const val TIMEOUT = 10000
|
private const val TIMEOUT = 10000
|
||||||
|
|
||||||
private val routeInfo =
|
private val routeInfo =
|
||||||
MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)
|
MediaRoute2Info.Builder(DEFAULT_ID, OTHER_DEVICE_NAME)
|
||||||
.addFeature("feature")
|
.addFeature("feature")
|
||||||
.setClientPackageName(PACKAGE_NAME)
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private val routeInfoWithBlankDeviceName =
|
private val routeInfoWithBlankDeviceName =
|
||||||
MediaRoute2Info.Builder("id", BLANK_DEVICE_NAME)
|
MediaRoute2Info.Builder(DEFAULT_ID, BLANK_DEVICE_NAME)
|
||||||
.addFeature("feature")
|
.addFeature("feature")
|
||||||
.setClientPackageName(PACKAGE_NAME)
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
underTest.displayView(getState())
|
underTest.displayView(getState())
|
||||||
assertThat(fakeWakeLock.isHeld).isTrue()
|
assertThat(fakeWakeLock.isHeld).isTrue()
|
||||||
|
|
||||||
underTest.removeView("id", "test reason")
|
underTest.removeView(DEFAULT_ID, "test reason")
|
||||||
|
|
||||||
assertThat(fakeWakeLock.isHeld).isFalse()
|
assertThat(fakeWakeLock.isHeld).isFalse()
|
||||||
}
|
}
|
||||||
@@ -175,6 +175,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun displayView_twiceWithDifferentIds_oldViewRemovedNewViewAdded() {
|
fun displayView_twiceWithDifferentIds_oldViewRemovedNewViewAdded() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "name",
|
name = "name",
|
||||||
@@ -199,10 +201,15 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
assertThat(windowParamsCaptor.allValues[0].title).isEqualTo("First Fake Window Title")
|
assertThat(windowParamsCaptor.allValues[0].title).isEqualTo("First Fake Window Title")
|
||||||
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
|
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
|
||||||
verify(windowManager).removeView(viewCaptor.allValues[0])
|
verify(windowManager).removeView(viewCaptor.allValues[0])
|
||||||
|
// Since the controller is still storing the older view in case it'll get re-displayed
|
||||||
|
// later, the listener shouldn't be notified
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun displayView_viewDoesNotDisappearsBeforeTimeout() {
|
fun displayView_viewDoesNotDisappearsBeforeTimeout() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
val state = getState()
|
val state = getState()
|
||||||
underTest.displayView(state)
|
underTest.displayView(state)
|
||||||
reset(windowManager)
|
reset(windowManager)
|
||||||
@@ -210,10 +217,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
fakeClock.advanceTime(TIMEOUT_MS - 1)
|
fakeClock.advanceTime(TIMEOUT_MS - 1)
|
||||||
|
|
||||||
verify(windowManager, never()).removeView(any())
|
verify(windowManager, never()).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun displayView_viewDisappearsAfterTimeout() {
|
fun displayView_viewDisappearsAfterTimeout() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
val state = getState()
|
val state = getState()
|
||||||
underTest.displayView(state)
|
underTest.displayView(state)
|
||||||
reset(windowManager)
|
reset(windowManager)
|
||||||
@@ -221,10 +231,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun displayView_calledAgainBeforeTimeout_timeoutReset() {
|
fun displayView_calledAgainBeforeTimeout_timeoutReset() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
// First, display the view
|
// First, display the view
|
||||||
val state = getState()
|
val state = getState()
|
||||||
underTest.displayView(state)
|
underTest.displayView(state)
|
||||||
@@ -239,10 +252,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
// Verify we didn't hide the view
|
// Verify we didn't hide the view
|
||||||
verify(windowManager, never()).removeView(any())
|
verify(windowManager, never()).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
|
fun displayView_calledAgainBeforeTimeout_eventuallyTimesOut() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
// First, display the view
|
// First, display the view
|
||||||
val state = getState()
|
val state = getState()
|
||||||
underTest.displayView(state)
|
underTest.displayView(state)
|
||||||
@@ -255,6 +271,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -270,26 +287,10 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun viewUpdatedWithNewOnViewTimeoutRunnable_newRunnableUsed() {
|
|
||||||
var runnable1Run = false
|
|
||||||
underTest.displayView(ViewInfo(name = "name", id = "id1", windowTitle = "1")) {
|
|
||||||
runnable1Run = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var runnable2Run = false
|
|
||||||
underTest.displayView(ViewInfo(name = "name", id = "id1", windowTitle = "1")) {
|
|
||||||
runnable2Run = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
|
||||||
|
|
||||||
assertThat(runnable1Run).isFalse()
|
|
||||||
assertThat(runnable2Run).isTrue()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViewsWithDifferentIds_moreRecentReplacesOlder() {
|
fun multipleViewsWithDifferentIds_moreRecentReplacesOlder() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "name",
|
name = "name",
|
||||||
@@ -315,10 +316,16 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
|
assertThat(windowParamsCaptor.allValues[1].title).isEqualTo("Second Fake Window Title")
|
||||||
verify(windowManager).removeView(viewCaptor.allValues[0])
|
verify(windowManager).removeView(viewCaptor.allValues[0])
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
|
|
||||||
|
// Since the controller is still storing the older view in case it'll get re-displayed
|
||||||
|
// later, the listener shouldn't be notified
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViewsWithDifferentIds_recentActiveViewIsDisplayed() {
|
fun multipleViewsWithDifferentIds_newViewRemoved_previousViewIsDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(ViewInfo("First name", id = "id1"))
|
underTest.displayView(ViewInfo("First name", id = "id1"))
|
||||||
|
|
||||||
verify(windowManager).addView(any(), any())
|
verify(windowManager).addView(any(), any())
|
||||||
@@ -329,24 +336,35 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
verify(windowManager).addView(any(), any())
|
verify(windowManager).addView(any(), any())
|
||||||
reset(windowManager)
|
reset(windowManager)
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
|
|
||||||
|
// WHEN the current view is removed
|
||||||
underTest.removeView("id2", "test reason")
|
underTest.removeView("id2", "test reason")
|
||||||
|
|
||||||
|
// THEN it's correctly removed
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("id2")
|
||||||
|
|
||||||
|
// And the previous view is correctly added
|
||||||
verify(windowManager).addView(any(), any())
|
verify(windowManager).addView(any(), any())
|
||||||
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
|
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
|
||||||
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
|
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
|
||||||
|
|
||||||
|
// WHEN the previous view times out
|
||||||
reset(windowManager)
|
reset(windowManager)
|
||||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||||
|
|
||||||
|
// THEN it is also removed
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(0)
|
assertThat(underTest.activeViews.size).isEqualTo(0)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id2", "id1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
|
fun multipleViewsWithDifferentIds_oldViewRemoved_recentViewIsDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(ViewInfo("First name", id = "id1"))
|
underTest.displayView(ViewInfo("First name", id = "id1"))
|
||||||
|
|
||||||
verify(windowManager).addView(any(), any())
|
verify(windowManager).addView(any(), any())
|
||||||
@@ -361,7 +379,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
// WHEN an old view is removed
|
// WHEN an old view is removed
|
||||||
underTest.removeView("id1", "test reason")
|
underTest.removeView("id1", "test reason")
|
||||||
|
|
||||||
// THEN we don't update anything
|
// THEN we don't update anything except the listener
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("id1")
|
||||||
verify(windowManager, never()).removeView(any())
|
verify(windowManager, never()).removeView(any())
|
||||||
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
|
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
|
||||||
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
||||||
@@ -372,10 +391,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(0)
|
assertThat(underTest.activeViews.size).isEqualTo(0)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id1", "id2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
|
fun multipleViewsWithDifferentIds_threeDifferentViews_recentActiveViewIsDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(ViewInfo("First name", id = "id1"))
|
underTest.displayView(ViewInfo("First name", id = "id1"))
|
||||||
underTest.displayView(ViewInfo("Second name", id = "id2"))
|
underTest.displayView(ViewInfo("Second name", id = "id2"))
|
||||||
underTest.displayView(ViewInfo("Third name", id = "id3"))
|
underTest.displayView(ViewInfo("Third name", id = "id3"))
|
||||||
@@ -387,6 +409,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
underTest.removeView("id3", "test reason")
|
underTest.removeView("id3", "test reason")
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3"))
|
||||||
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
|
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id2")
|
||||||
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("Second name")
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
@@ -395,6 +418,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
underTest.removeView("id2", "test reason")
|
underTest.removeView("id2", "test reason")
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3", "id2"))
|
||||||
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
|
assertThat(underTest.mostRecentViewInfo?.id).isEqualTo("id1")
|
||||||
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
|
assertThat(underTest.mostRecentViewInfo?.name).isEqualTo("First name")
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
@@ -403,6 +427,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEqualTo(listOf("id3", "id2", "id1"))
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(0)
|
assertThat(underTest.activeViews.size).isEqualTo(0)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
}
|
}
|
||||||
@@ -438,6 +463,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViews_mostRecentViewRemoved_otherViewsTimedOutAndNotDisplayed() {
|
fun multipleViews_mostRecentViewRemoved_otherViewsTimedOutAndNotDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
|
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
|
||||||
fakeClock.advanceTime(1000)
|
fakeClock.advanceTime(1000)
|
||||||
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 4000))
|
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 4000))
|
||||||
@@ -451,10 +478,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager, never()).addView(any(), any())
|
verify(windowManager, never()).addView(any(), any())
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(0)
|
assertThat(underTest.activeViews.size).isEqualTo(0)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2", "id3")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun multipleViews_mostRecentViewRemoved_viewWithShortTimeLeftNotDisplayed() {
|
fun multipleViews_mostRecentViewRemoved_viewWithShortTimeLeftNotDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
|
underTest.displayView(ViewInfo("First name", id = "id1", timeoutMs = 4000))
|
||||||
fakeClock.advanceTime(1000)
|
fakeClock.advanceTime(1000)
|
||||||
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 2500))
|
underTest.displayView(ViewInfo("Second name", id = "id2", timeoutMs = 2500))
|
||||||
@@ -467,10 +497,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager, never()).addView(any(), any())
|
verify(windowManager, never()).addView(any(), any())
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(0)
|
assertThat(underTest.activeViews.size).isEqualTo(0)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun lowerThenHigherPriority_higherReplacesLower() {
|
fun lowerThenHigherPriority_higherReplacesLower() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "normal",
|
name = "normal",
|
||||||
@@ -499,10 +532,15 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
|
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
|
||||||
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title")
|
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title")
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
|
// Since the controller is still storing the older view in case it'll get re-displayed
|
||||||
|
// later, the listener shouldn't be notified
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun lowerThenHigherPriority_lowerPriorityRedisplayed() {
|
fun lowerThenHigherPriority_lowerPriorityRedisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "normal",
|
name = "normal",
|
||||||
@@ -537,6 +575,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
// THEN the normal view is re-displayed
|
// THEN the normal view is re-displayed
|
||||||
verify(windowManager).removeView(viewCaptor.allValues[1])
|
verify(windowManager).removeView(viewCaptor.allValues[1])
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("critical")
|
||||||
verify(windowManager).addView(any(), capture(windowParamsCaptor))
|
verify(windowManager).addView(any(), capture(windowParamsCaptor))
|
||||||
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
|
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
@@ -544,6 +583,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun lowerThenHigherPriority_lowerPriorityNotRedisplayedBecauseTimedOut() {
|
fun lowerThenHigherPriority_lowerPriorityNotRedisplayedBecauseTimedOut() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "normal",
|
name = "normal",
|
||||||
@@ -573,6 +614,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager, never()).addView(any(), any())
|
verify(windowManager, never()).addView(any(), any())
|
||||||
assertThat(underTest.activeViews).isEmpty()
|
assertThat(underTest.activeViews).isEmpty()
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("critical", "normal")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -609,6 +651,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun higherThenLowerPriority_lowerEventuallyDisplayed() {
|
fun higherThenLowerPriority_lowerEventuallyDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "critical",
|
name = "critical",
|
||||||
@@ -644,6 +688,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
// THEN the second normal view is displayed
|
// THEN the second normal view is displayed
|
||||||
verify(windowManager).removeView(viewCaptor.value)
|
verify(windowManager).removeView(viewCaptor.value)
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("critical")
|
||||||
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
|
verify(windowManager).addView(capture(viewCaptor), capture(windowParamsCaptor))
|
||||||
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
|
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title")
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(1)
|
assertThat(underTest.activeViews.size).isEqualTo(1)
|
||||||
@@ -652,6 +697,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun higherThenLowerPriority_lowerNotDisplayedBecauseTimedOut() {
|
fun higherThenLowerPriority_lowerNotDisplayedBecauseTimedOut() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "critical",
|
name = "critical",
|
||||||
@@ -691,10 +738,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
verify(windowManager, never()).addView(any(), any())
|
verify(windowManager, never()).addView(any(), any())
|
||||||
assertThat(underTest.activeViews).isEmpty()
|
assertThat(underTest.activeViews).isEmpty()
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("critical", "normal")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun criticalThenNewCritical_newCriticalDisplayed() {
|
fun criticalThenNewCritical_newCriticalDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "critical 1",
|
name = "critical 1",
|
||||||
@@ -724,10 +774,15 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title 2")
|
assertThat(windowParamsCaptor.value.title).isEqualTo("Critical Window Title 2")
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(2)
|
assertThat(underTest.activeViews.size).isEqualTo(2)
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
|
// Since the controller is still storing the older view in case it'll get re-displayed
|
||||||
|
// later, the listener shouldn't be notified
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun normalThenNewNormal_newNormalDisplayed() {
|
fun normalThenNewNormal_newNormalDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.displayView(
|
underTest.displayView(
|
||||||
ViewInfo(
|
ViewInfo(
|
||||||
name = "normal 1",
|
name = "normal 1",
|
||||||
@@ -757,6 +812,9 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title 2")
|
assertThat(windowParamsCaptor.value.title).isEqualTo("Normal Window Title 2")
|
||||||
assertThat(underTest.activeViews.size).isEqualTo(2)
|
assertThat(underTest.activeViews.size).isEqualTo(2)
|
||||||
verify(configurationController, never()).removeCallback(any())
|
verify(configurationController, never()).removeCallback(any())
|
||||||
|
// Since the controller is still storing the older view in case it'll get re-displayed
|
||||||
|
// later, the listener shouldn't be notified
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -957,25 +1015,103 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeView_viewRemovedAndRemovalLogged() {
|
fun removeView_viewRemovedAndRemovalLoggedAndListenerNotified() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
// First, add the view
|
// First, add the view
|
||||||
underTest.displayView(getState())
|
underTest.displayView(getState())
|
||||||
|
|
||||||
// Then, remove it
|
// Then, remove it
|
||||||
val reason = "test reason"
|
val reason = "test reason"
|
||||||
val deviceId = "id"
|
underTest.removeView(DEFAULT_ID, reason)
|
||||||
underTest.removeView(deviceId, reason)
|
|
||||||
|
|
||||||
verify(windowManager).removeView(any())
|
verify(windowManager).removeView(any())
|
||||||
verify(logger).logViewRemoval(deviceId, reason)
|
verify(logger).logViewRemoval(DEFAULT_ID, reason)
|
||||||
verify(configurationController).removeCallback(any())
|
verify(configurationController).removeCallback(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun removeView_noAdd_viewNotRemoved() {
|
fun removeView_noAdd_viewNotRemovedAndListenerNotNotified() {
|
||||||
|
val listener = registerListener()
|
||||||
|
|
||||||
underTest.removeView("id", "reason")
|
underTest.removeView("id", "reason")
|
||||||
|
|
||||||
verify(windowManager, never()).removeView(any())
|
verify(windowManager, never()).removeView(any())
|
||||||
|
assertThat(listener.permanentlyRemovedIds).isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listenerRegistered_notifiedOnRemoval() {
|
||||||
|
val listener = registerListener()
|
||||||
|
underTest.displayView(getState())
|
||||||
|
|
||||||
|
underTest.removeView(DEFAULT_ID, "reason")
|
||||||
|
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun listenerRegistered_notifiedOnTimedOutEvenWhenNotDisplayed() {
|
||||||
|
val listener = registerListener()
|
||||||
|
underTest.displayView(
|
||||||
|
ViewInfo(
|
||||||
|
id = "id1",
|
||||||
|
name = "name1",
|
||||||
|
timeoutMs = 3000,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Display a second view
|
||||||
|
underTest.displayView(
|
||||||
|
ViewInfo(
|
||||||
|
id = "id2",
|
||||||
|
name = "name2",
|
||||||
|
timeoutMs = 2500,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN the second view times out
|
||||||
|
fakeClock.advanceTime(2501)
|
||||||
|
|
||||||
|
// THEN the listener is notified of both IDs, since id2 timed out and id1 doesn't have
|
||||||
|
// enough time left to be redisplayed
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly("id1", "id2")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multipleListeners_allNotified() {
|
||||||
|
val listener1 = registerListener()
|
||||||
|
val listener2 = registerListener()
|
||||||
|
val listener3 = registerListener()
|
||||||
|
|
||||||
|
underTest.displayView(getState())
|
||||||
|
|
||||||
|
underTest.removeView(DEFAULT_ID, "reason")
|
||||||
|
|
||||||
|
assertThat(listener1.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
|
assertThat(listener2.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
|
assertThat(listener3.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sameListenerRegisteredMultipleTimes_onlyNotifiedOnce() {
|
||||||
|
val listener = registerListener()
|
||||||
|
underTest.registerListener(listener)
|
||||||
|
underTest.registerListener(listener)
|
||||||
|
|
||||||
|
underTest.displayView(getState())
|
||||||
|
|
||||||
|
underTest.removeView(DEFAULT_ID, "reason")
|
||||||
|
|
||||||
|
assertThat(listener.permanentlyRemovedIds).hasSize(1)
|
||||||
|
assertThat(listener.permanentlyRemovedIds).containsExactly(DEFAULT_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerListener(): Listener {
|
||||||
|
return Listener().also {
|
||||||
|
underTest.registerListener(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getState(name: String = "name") = ViewInfo(name)
|
private fun getState(name: String = "name") = ViewInfo(name)
|
||||||
@@ -1030,9 +1166,17 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
override val windowTitle: String = "Window Title",
|
override val windowTitle: String = "Window Title",
|
||||||
override val wakeReason: String = "WAKE_REASON",
|
override val wakeReason: String = "WAKE_REASON",
|
||||||
override val timeoutMs: Int = TIMEOUT_MS.toInt(),
|
override val timeoutMs: Int = TIMEOUT_MS.toInt(),
|
||||||
override val id: String = "id",
|
override val id: String = DEFAULT_ID,
|
||||||
override val priority: ViewPriority = ViewPriority.NORMAL,
|
override val priority: ViewPriority = ViewPriority.NORMAL,
|
||||||
) : TemporaryViewInfo()
|
) : TemporaryViewInfo()
|
||||||
|
|
||||||
|
inner class Listener : TemporaryViewDisplayController.Listener {
|
||||||
|
val permanentlyRemovedIds = mutableListOf<String>()
|
||||||
|
override fun onInfoPermanentlyRemoved(id: String) {
|
||||||
|
permanentlyRemovedIds.add(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val TIMEOUT_MS = 10000L
|
private const val TIMEOUT_MS = 10000L
|
||||||
|
private const val DEFAULT_ID = "defaultId"
|
||||||
|
|||||||
Reference in New Issue
Block a user