Merge changes I58955fd8,Iac4d46ec,I9487cfdc into tm-qpr-dev
* changes: [Media TTT] Remove `displayedState` and always use the map instead. [Media TTT] If the FAR state is ignored, don't update the store. [Media TTT] Log changes to the sender's current state map.
This commit is contained in:
committed by
Android (Google) Code Review
commit
281d344bbc
@@ -23,9 +23,11 @@ import android.view.View
|
|||||||
import com.android.internal.logging.UiEventLogger
|
import com.android.internal.logging.UiEventLogger
|
||||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||||
import com.android.systemui.CoreStartable
|
import com.android.systemui.CoreStartable
|
||||||
|
import com.android.systemui.Dumpable
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.common.shared.model.Text
|
import com.android.systemui.common.shared.model.Text
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dump.DumpManager
|
||||||
import com.android.systemui.media.taptotransfer.MediaTttFlags
|
import com.android.systemui.media.taptotransfer.MediaTttFlags
|
||||||
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
|
||||||
@@ -34,6 +36,7 @@ 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
|
||||||
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
|
import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
|
||||||
|
import java.io.PrintWriter
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,14 +50,13 @@ constructor(
|
|||||||
private val chipbarCoordinator: ChipbarCoordinator,
|
private val chipbarCoordinator: ChipbarCoordinator,
|
||||||
private val commandQueue: CommandQueue,
|
private val commandQueue: CommandQueue,
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
private val dumpManager: DumpManager,
|
||||||
private val logger: MediaTttSenderLogger,
|
private val logger: MediaTttSenderLogger,
|
||||||
private val mediaTttFlags: MediaTttFlags,
|
private val mediaTttFlags: MediaTttFlags,
|
||||||
private val uiEventLogger: MediaTttSenderUiEventLogger,
|
private val uiEventLogger: MediaTttSenderUiEventLogger,
|
||||||
) : CoreStartable {
|
) : CoreStartable, Dumpable {
|
||||||
|
|
||||||
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 =
|
||||||
@@ -75,6 +77,7 @@ constructor(
|
|||||||
override fun start() {
|
override fun start() {
|
||||||
if (mediaTttFlags.isMediaTttEnabled()) {
|
if (mediaTttFlags.isMediaTttEnabled()) {
|
||||||
commandQueue.addCallback(commandQueueCallbacks)
|
commandQueue.addCallback(commandQueueCallbacks)
|
||||||
|
dumpManager.registerNormalDumpable(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,11 +95,11 @@ constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentState = stateMap[routeInfo.id]
|
val currentStateForId: ChipStateSender? = stateMap[routeInfo.id]
|
||||||
if (!ChipStateSender.isValidStateTransition(currentState, chipState)) {
|
if (!ChipStateSender.isValidStateTransition(currentStateForId, chipState)) {
|
||||||
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
|
// ChipStateSender.FAR_FROM_RECEIVER is the default state when there is no state.
|
||||||
logger.logInvalidStateTransitionError(
|
logger.logInvalidStateTransitionError(
|
||||||
currentState = currentState?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
|
currentState = currentStateForId?.name ?: ChipStateSender.FAR_FROM_RECEIVER.name,
|
||||||
chipState.name
|
chipState.name
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -104,30 +107,29 @@ constructor(
|
|||||||
uiEventLogger.logSenderStateChange(chipState)
|
uiEventLogger.logSenderStateChange(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
|
// Return early if we're not displaying a chip for this ID anyway
|
||||||
removeIdFromStore(routeInfo.id)
|
if (currentStateForId == null) return
|
||||||
// Return early if we're not displaying a chip anyway
|
|
||||||
val currentDisplayedState = displayedState ?: return
|
|
||||||
|
|
||||||
val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
|
val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
|
||||||
if (
|
if (
|
||||||
currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
|
currentStateForId.transferStatus == TransferStatus.IN_PROGRESS ||
|
||||||
currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
|
currentStateForId.transferStatus == TransferStatus.SUCCEEDED
|
||||||
) {
|
) {
|
||||||
// Don't remove the chip if we're in progress or succeeded, since the user should
|
// Don't remove the chip if we're in progress or succeeded, since the user should
|
||||||
// still be able to see the status of the transfer.
|
// still be able to see the status of the transfer.
|
||||||
logger.logRemovalBypass(
|
logger.logRemovalBypass(
|
||||||
removalReason,
|
removalReason,
|
||||||
bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
|
bypassReason = "transferStatus=${currentStateForId.transferStatus.name}"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
displayedState = null
|
// No need to store the state since it is the default state
|
||||||
|
removeIdFromStore(routeInfo.id, reason = removalReason)
|
||||||
chipbarCoordinator.removeView(routeInfo.id, removalReason)
|
chipbarCoordinator.removeView(routeInfo.id, removalReason)
|
||||||
} else {
|
} else {
|
||||||
stateMap[routeInfo.id] = chipState
|
stateMap[routeInfo.id] = chipState
|
||||||
displayedState = chipState
|
logger.logStateMap(stateMap)
|
||||||
chipbarCoordinator.registerListener(displayListener)
|
chipbarCoordinator.registerListener(displayListener)
|
||||||
chipbarCoordinator.displayView(
|
chipbarCoordinator.displayView(
|
||||||
createChipbarInfo(
|
createChipbarInfo(
|
||||||
@@ -232,12 +234,19 @@ constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val displayListener =
|
private val displayListener =
|
||||||
TemporaryViewDisplayController.Listener { id -> removeIdFromStore(id) }
|
TemporaryViewDisplayController.Listener { id, reason -> removeIdFromStore(id, reason) }
|
||||||
|
|
||||||
private fun removeIdFromStore(id: String) {
|
private fun removeIdFromStore(id: String, reason: String) {
|
||||||
|
logger.logStateMapRemoval(id, reason)
|
||||||
stateMap.remove(id)
|
stateMap.remove(id)
|
||||||
|
logger.logStateMap(stateMap)
|
||||||
if (stateMap.isEmpty()) {
|
if (stateMap.isEmpty()) {
|
||||||
chipbarCoordinator.unregisterListener(displayListener)
|
chipbarCoordinator.unregisterListener(displayListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
|
pw.println("Current sender states:")
|
||||||
|
pw.println(stateMap.toString())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,29 @@ constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Logs the current contents of the state map. */
|
||||||
|
fun logStateMap(map: Map<String, ChipStateSender>) {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
{ str1 = map.toString() },
|
||||||
|
{ "Current sender states: $str1" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Logs that [id] has been removed from the state map due to [reason]. */
|
||||||
|
fun logStateMapRemoval(id: String, reason: String) {
|
||||||
|
buffer.log(
|
||||||
|
TAG,
|
||||||
|
LogLevel.DEBUG,
|
||||||
|
{
|
||||||
|
str1 = id
|
||||||
|
str2 = reason
|
||||||
|
},
|
||||||
|
{ "State removal: id=$str1 reason=$str2" }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MediaTttSender"
|
private const val TAG = "MediaTttSender"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
// appropriately.
|
// appropriately.
|
||||||
activeViews.remove(displayInfo)
|
activeViews.remove(displayInfo)
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
it.onInfoPermanentlyRemoved(id)
|
it.onInfoPermanentlyRemoved(id, removalReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to time the view out since it's already gone
|
// No need to time the view out since it's already gone
|
||||||
@@ -393,7 +393,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
activeViews.remove(it)
|
activeViews.remove(it)
|
||||||
logger.logViewExpiration(it.info)
|
logger.logViewExpiration(it.info)
|
||||||
listeners.forEach { listener ->
|
listeners.forEach { listener ->
|
||||||
listener.onInfoPermanentlyRemoved(it.info.id)
|
listener.onInfoPermanentlyRemoved(it.info.id, REMOVAL_REASON_TIME_EXPIRED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,7 +457,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
* Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
|
* Called whenever a [DisplayInfo] with the given [id] has been removed and will never be
|
||||||
* displayed again (unless another call to [updateView] is made).
|
* displayed again (unless another call to [updateView] is made).
|
||||||
*/
|
*/
|
||||||
fun onInfoPermanentlyRemoved(id: String)
|
fun onInfoPermanentlyRemoved(id: String, reason: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A container for all the display-related state objects. */
|
/** A container for all the display-related state objects. */
|
||||||
@@ -494,6 +494,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
|
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
|
||||||
|
private const val REMOVAL_REASON_TIME_EXPIRED = "TIMEOUT_EXPIRED_BEFORE_REDISPLAY"
|
||||||
private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
|
private const val MIN_REQUIRED_TIME_FOR_REDISPLAY = 1000
|
||||||
|
|
||||||
private data class IconInfo(
|
private data class IconInfo(
|
||||||
|
|||||||
@@ -161,6 +161,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
chipbarCoordinator,
|
chipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -179,6 +180,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
chipbarCoordinator,
|
chipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -540,6 +542,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
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())
|
||||||
verify(windowManager).removeView(viewCaptor.value)
|
verify(windowManager).removeView(viewCaptor.value)
|
||||||
|
verify(logger).logStateMapRemoval(eq(DEFAULT_ID), any())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -739,6 +742,99 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(windowManager, never()).addView(any(), any())
|
verify(windowManager, never()).addView(any(), any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266217596. */
|
||||||
|
@Test
|
||||||
|
fun toReceiver_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN a FAR command comes in
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it is ignored and the chipbar is stilled displayed
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
|
||||||
|
verify(windowManager, never()).removeView(any())
|
||||||
|
|
||||||
|
// WHEN a SUCCEEDED command comes in
|
||||||
|
val succeededRouteInfo =
|
||||||
|
MediaRoute2Info.Builder(DEFAULT_ID, "Tablet Succeeded")
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||||
|
succeededRouteInfo,
|
||||||
|
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
|
||||||
|
override fun onUndoTriggered() {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
|
||||||
|
// state. (The "invalid transition" would be FAR => SUCCEEDED.)
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(
|
||||||
|
ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.getExpectedStateText(
|
||||||
|
"Tablet Succeeded"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
|
||||||
|
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266217596. */
|
||||||
|
@Test
|
||||||
|
fun toThisDevice_triggeredThenFar_thenSucceeded_updatesToSucceeded() {
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN a FAR command comes in
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it is ignored and the chipbar is stilled displayed
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText())
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
|
||||||
|
verify(windowManager, never()).removeView(any())
|
||||||
|
|
||||||
|
// WHEN a SUCCEEDED command comes in
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
|
||||||
|
routeInfo,
|
||||||
|
/* undoCallback= */ object : IUndoMediaTransferCallback.Stub() {
|
||||||
|
override fun onUndoTriggered() {}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it is *not* marked as an invalid transition and the chipbar updates to the succeeded
|
||||||
|
// state. (The "invalid transition" would be FAR => SUCCEEDED.)
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(
|
||||||
|
ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.getExpectedStateText(
|
||||||
|
"Tablet Succeeded"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.GONE)
|
||||||
|
assertThat(chipbarView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun receivesNewStateFromCommandQueue_isLogged() {
|
fun receivesNewStateFromCommandQueue_isLogged() {
|
||||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
@@ -932,6 +1028,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
mockChipbarCoordinator,
|
mockChipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -958,6 +1055,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
mockChipbarCoordinator,
|
mockChipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -975,9 +1073,10 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
// WHEN the listener is notified that the view has been removed
|
// WHEN the listener is notified that the view has been removed
|
||||||
listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID)
|
listenerCaptor.value.onInfoPermanentlyRemoved(DEFAULT_ID, "reason")
|
||||||
|
|
||||||
// THEN the media coordinator unregisters the listener
|
// THEN the media coordinator unregisters the listener
|
||||||
|
verify(logger).logStateMapRemoval(DEFAULT_ID, "reason")
|
||||||
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
|
verify(mockChipbarCoordinator).unregisterListener(listenerCaptor.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -989,6 +1088,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
mockChipbarCoordinator,
|
mockChipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -1006,7 +1106,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
verify(mockChipbarCoordinator).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
// WHEN the listener is notified that a different view has been removed
|
// WHEN the listener is notified that a different view has been removed
|
||||||
listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId")
|
listenerCaptor.value.onInfoPermanentlyRemoved("differentViewId", "reason")
|
||||||
|
|
||||||
// THEN the media coordinator doesn't unregister the listener
|
// THEN the media coordinator doesn't unregister the listener
|
||||||
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
||||||
@@ -1020,6 +1120,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
mockChipbarCoordinator,
|
mockChipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -1055,6 +1156,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
mockChipbarCoordinator,
|
mockChipbarCoordinator,
|
||||||
commandQueue,
|
commandQueue,
|
||||||
context,
|
context,
|
||||||
|
dumpManager,
|
||||||
logger,
|
logger,
|
||||||
mediaTttFlags,
|
mediaTttFlags,
|
||||||
uiEventLogger,
|
uiEventLogger,
|
||||||
@@ -1084,10 +1186,173 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
|
verify(mockChipbarCoordinator, atLeast(1)).registerListener(capture(listenerCaptor))
|
||||||
|
|
||||||
// THEN one of them is removed
|
// THEN one of them is removed
|
||||||
listenerCaptor.value.onInfoPermanentlyRemoved("route1")
|
listenerCaptor.value.onInfoPermanentlyRemoved("route1", "reason")
|
||||||
|
|
||||||
// THEN the media coordinator doesn't unregister the listener (since route2 is still active)
|
// THEN the media coordinator doesn't unregister the listener (since route2 is still active)
|
||||||
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
verify(mockChipbarCoordinator, never()).unregisterListener(listenerCaptor.value)
|
||||||
|
verify(logger).logStateMapRemoval("route1", "reason")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266218672. */
|
||||||
|
@Test
|
||||||
|
fun twoIdsDisplayed_oldIdIsFar_viewStillDisplayed() {
|
||||||
|
// WHEN there are two different media transfers with different IDs
|
||||||
|
val route1 =
|
||||||
|
MediaRoute2Info.Builder("route1", OTHER_DEVICE_NAME)
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
route1,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
verify(windowManager).addView(any(), any())
|
||||||
|
reset(windowManager)
|
||||||
|
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
|
||||||
|
MediaRoute2Info.Builder("route2", "Route 2 name")
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build(),
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
val newView = getChipbarView()
|
||||||
|
|
||||||
|
// WHEN there's a FAR event for the earlier one
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||||
|
route1,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it's ignored and the more recent one is still displayed
|
||||||
|
assertThat(newView.getChipText())
|
||||||
|
.isEqualTo(
|
||||||
|
ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText("Route 2 name")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266218672. */
|
||||||
|
@Test
|
||||||
|
fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToEnd() {
|
||||||
|
displayReceiverTriggered()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||||
|
verify(windowManager).removeView(any())
|
||||||
|
|
||||||
|
reset(windowManager)
|
||||||
|
|
||||||
|
// WHEN we try to show ALMOST_CLOSE_TO_END
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it succeeds
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.getExpectedStateText())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266218672. */
|
||||||
|
@Test
|
||||||
|
fun receiverSucceededThenTimedOut_internalStateResetAndCanDisplayReceiverTriggered() {
|
||||||
|
displayReceiverTriggered()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||||
|
verify(windowManager).removeView(any())
|
||||||
|
|
||||||
|
reset(windowManager)
|
||||||
|
|
||||||
|
// WHEN we try to show RECEIVER_TRIGGERED
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it succeeds
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.getExpectedStateText())
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266218672. */
|
||||||
|
@Test
|
||||||
|
fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayAlmostCloseToStart() {
|
||||||
|
displayThisDeviceTriggered()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||||
|
verify(windowManager).removeView(any())
|
||||||
|
|
||||||
|
reset(windowManager)
|
||||||
|
|
||||||
|
// WHEN we try to show ALMOST_CLOSE_TO_START
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it succeeds
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.getExpectedStateText())
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Regression test for b/266218672. */
|
||||||
|
@Test
|
||||||
|
fun toThisDeviceSucceededThenTimedOut_internalStateResetAndCanDisplayThisDeviceTriggered() {
|
||||||
|
displayThisDeviceTriggered()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
|
||||||
|
routeInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||||
|
verify(windowManager).removeView(any())
|
||||||
|
|
||||||
|
reset(windowManager)
|
||||||
|
|
||||||
|
// WHEN we try to show THIS_DEVICE_TRIGGERED
|
||||||
|
val newRouteInfo =
|
||||||
|
MediaRoute2Info.Builder(DEFAULT_ID, "New Name")
|
||||||
|
.addFeature("feature")
|
||||||
|
.setClientPackageName(PACKAGE_NAME)
|
||||||
|
.build()
|
||||||
|
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||||
|
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||||
|
newRouteInfo,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN it succeeds
|
||||||
|
val chipbarView = getChipbarView()
|
||||||
|
assertThat(chipbarView.getChipText())
|
||||||
|
.isEqualTo(
|
||||||
|
ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.getExpectedStateText("New Name")
|
||||||
|
)
|
||||||
|
assertThat(chipbarView.getLoadingIcon().visibility).isEqualTo(View.VISIBLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getChipbarView(): ViewGroup {
|
private fun getChipbarView(): ViewGroup {
|
||||||
@@ -1107,8 +1372,10 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
|
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
|
||||||
|
|
||||||
private fun ChipStateSender.getExpectedStateText(): String? {
|
private fun ChipStateSender.getExpectedStateText(
|
||||||
return this.getChipTextString(context, OTHER_DEVICE_NAME).loadText(context)
|
otherDeviceName: String = OTHER_DEVICE_NAME,
|
||||||
|
): String? {
|
||||||
|
return this.getChipTextString(context, otherDeviceName).loadText(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
// display receiver triggered state helper method to make sure we start from a valid state
|
// display receiver triggered state helper method to make sure we start from a valid state
|
||||||
|
|||||||
@@ -87,6 +87,32 @@ class MediaTttSenderLoggerTest : SysuiTestCase() {
|
|||||||
assertThat(actualString).contains(bypassReason)
|
assertThat(actualString).contains(bypassReason)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun logStateMap_bufferHasInfo() {
|
||||||
|
val map =
|
||||||
|
mapOf(
|
||||||
|
"123" to ChipStateSender.ALMOST_CLOSE_TO_START_CAST,
|
||||||
|
"456" to ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.logStateMap(map)
|
||||||
|
|
||||||
|
val actualString = getStringFromBuffer()
|
||||||
|
assertThat(actualString).contains("123")
|
||||||
|
assertThat(actualString).contains(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
|
||||||
|
assertThat(actualString).contains("456")
|
||||||
|
assertThat(actualString).contains(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun logStateMapRemoval_bufferHasInfo() {
|
||||||
|
logger.logStateMapRemoval("456", "testReason")
|
||||||
|
|
||||||
|
val actualString = getStringFromBuffer()
|
||||||
|
assertThat(actualString).contains("456")
|
||||||
|
assertThat(actualString).contains("testReason")
|
||||||
|
}
|
||||||
|
|
||||||
private fun getStringFromBuffer(): String {
|
private fun getStringFromBuffer(): String {
|
||||||
val stringWriter = StringWriter()
|
val stringWriter = StringWriter()
|
||||||
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
|
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
|
||||||
|
|||||||
@@ -1172,7 +1172,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
|||||||
|
|
||||||
inner class Listener : TemporaryViewDisplayController.Listener {
|
inner class Listener : TemporaryViewDisplayController.Listener {
|
||||||
val permanentlyRemovedIds = mutableListOf<String>()
|
val permanentlyRemovedIds = mutableListOf<String>()
|
||||||
override fun onInfoPermanentlyRemoved(id: String) {
|
override fun onInfoPermanentlyRemoved(id: String, reason: String) {
|
||||||
permanentlyRemovedIds.add(id)
|
permanentlyRemovedIds.add(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user