Merge changes Ic4074394,I1be9f1ab

* changes:
  [Media TTT] Add error state.
  [Media TTT] Add the #closeToReceiverToEndCast callback.
This commit is contained in:
TreeHugger Robot
2022-01-24 19:36:24 +00:00
committed by Android (Google) Code Review
12 changed files with 273 additions and 20 deletions

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="@android:color/white" android:pathData="M12,12.5zM1,21L12,2l11,19zM11,15h2v-5h-2zM12,18q0.425,0 0.713,-0.288Q13,17.425 13,17t-0.287,-0.712Q12.425,16 12,16t-0.713,0.288Q11,16.575 11,17t0.287,0.712Q11.575,18 12,18zM4.45,19h15.1L12,6z"/>
</vector>

View File

@@ -28,8 +28,8 @@
<com.android.internal.widget.CachingIconView
android:id="@+id/app_icon"
android:layout_width="@dimen/media_ttt_icon_size"
android:layout_height="@dimen/media_ttt_icon_size"
android:layout_width="@dimen/media_ttt_app_icon_size"
android:layout_height="@dimen/media_ttt_app_icon_size"
android:layout_marginEnd="12dp"
/>
@@ -41,23 +41,34 @@
android:textColor="?android:attr/textColorPrimary"
/>
<!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
<ProgressBar
android:id="@+id/loading"
android:indeterminate="true"
android:layout_width="@dimen/media_ttt_loading_size"
android:layout_height="@dimen/media_ttt_loading_size"
android:layout_marginStart="12dp"
android:layout_width="@dimen/media_ttt_status_icon_size"
android:layout_height="@dimen/media_ttt_status_icon_size"
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
style="?android:attr/progressBarStyleSmall"
/>
<ImageView
android:id="@+id/failure_icon"
android:layout_width="@dimen/media_ttt_status_icon_size"
android:layout_height="@dimen/media_ttt_status_icon_size"
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:src="@drawable/ic_warning"
android:tint="@color/GM2_red_500"
/>
<TextView
android:id="@+id/undo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/media_transfer_undo"
android:textColor="?androidprv:attr/textColorOnAccent"
android:layout_marginStart="12dp"
android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
android:textSize="@dimen/media_ttt_text_size"
android:paddingStart="@dimen/media_ttt_chip_outer_padding"
android:paddingEnd="@dimen/media_ttt_chip_outer_padding"

View File

@@ -984,10 +984,11 @@
<!-- Media tap-to-transfer chip for sender device -->
<dimen name="media_ttt_chip_outer_padding">16dp</dimen>
<dimen name="media_ttt_text_size">16sp</dimen>
<dimen name="media_ttt_icon_size">24dp</dimen>
<dimen name="media_ttt_loading_size">20dp</dimen>
<dimen name="media_ttt_app_icon_size">24dp</dimen>
<dimen name="media_ttt_status_icon_size">20dp</dimen>
<dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
<dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
<dimen name="media_ttt_last_item_start_margin">12dp</dimen>
<!-- Media tap-to-transfer chip for receiver device -->
<dimen name="media_ttt_chip_size_receiver">100dp</dimen>

View File

@@ -2158,8 +2158,12 @@
<string name="media_transfer_undo">Undo</string>
<!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play media on the different device. [CHAR LIMIT=75] -->
<string name="media_move_closer_to_start_cast">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
<!-- Text to ask the user to move their device closer to a different device (deviceName) in order to transfer media from the different device and back onto the current device. [CHAR LIMIT=75] -->
<string name="media_move_closer_to_end_cast">Move closer to <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g> to play here</string>
<!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
<string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
<!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] -->
<string name="media_transfer_failed">Something went wrong</string>
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string>

View File

@@ -43,4 +43,28 @@ interface IDeviceSenderCallback {
*/
oneway void closeToReceiverToStartCast(
in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
/**
* Invoke to notify System UI that this device (the sender) is close to a receiver device, so
* the user can potentially *end* a cast on the receiver device if the user moves this device a
* bit closer.
*
* Important notes:
* - When this callback triggers, the device is close enough to inform the user that
* transferring is an option, but the device is *not* close enough to actually initiate a
* transfer yet.
* - This callback is for *ending* a cast. It should be used when media is currently being
* played on the receiver device and the media should be transferred to play locally
* instead.
*/
oneway void closeToReceiverToEndCast(
in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
/**
* Invoke to notify System UI that the attempted transfer has failed.
*
* This callback will be used for both the transfer that should've *started* playing the media
* on the receiver and the transfer that should've *ended* the playing on the receiver.
*/
oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
}

View File

@@ -33,7 +33,9 @@ import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerR
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender
import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferInitiated
import com.android.systemui.media.taptotransfer.sender.TransferSucceeded
import com.android.systemui.shared.mediattt.DeviceInfo
@@ -90,6 +92,11 @@ class MediaTttCommandLineHelper @Inject constructor(
senderCallback.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
}
}
MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
runOnService { senderCallback ->
senderCallback.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
}
}
// TODO(b/203800643): Migrate other commands to invoke the service instead of the
// controller.
@@ -116,11 +123,18 @@ class MediaTttCommandLineHelper @Inject constructor(
)
)
}
TRANSFER_FAILED_COMMAND_NAME -> {
runOnService { senderCallback ->
senderCallback.transferFailed(mediaInfo, otherDeviceInfo)
}
}
else -> {
pw.println("Chip type must be one of " +
"$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
"$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
"$TRANSFER_INITIATED_COMMAND_NAME, " +
TRANSFER_SUCCEEDED_COMMAND_NAME
"$TRANSFER_SUCCEEDED_COMMAND_NAME, " +
TRANSFER_FAILED_COMMAND_NAME
)
}
}
@@ -226,9 +240,13 @@ const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
@VisibleForTesting
val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
@VisibleForTesting
val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName!!
@VisibleForTesting
val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
@VisibleForTesting
val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
private const val FUTURE_WAIT_TIME = 2000L
private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"

View File

@@ -55,6 +55,22 @@ class MoveCloserToStartCast(
otherDeviceName
)
/**
* A state representing that the two devices are close but not close enough to *end* a cast that's
* currently occurring the receiver device. The chip will instruct the user to move closer in order
* to initiate the transfer from the receiver and back onto this device (the original sender).
*/
class MoveCloserToEndCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
otherDeviceName: String,
) : ChipStateSender(
appIconDrawable,
appIconContentDescription,
R.string.media_move_closer_to_end_cast,
otherDeviceName
)
/**
* A state representing that a transfer has been initiated (but not completed).
*
@@ -91,3 +107,17 @@ class TransferSucceeded(
R.string.media_transfer_playing,
otherDeviceName
)
/** A state representing that a transfer has failed. */
class TransferFailed(
appIconDrawable: Drawable,
appIconContentDescription: String,
// TODO(b/211493953): The failed chip doesn't need [otherDeviceName] so we may want to remove
// [otherDeviceName] from the superclass [ChipStateSender].
otherDeviceName: String,
) : ChipStateSender(
appIconDrawable,
appIconContentDescription,
R.string.media_transfer_failed,
otherDeviceName
)

View File

@@ -73,6 +73,11 @@ class MediaTttChipControllerSender @Inject constructor(
}
undoView.setOnClickListener(undoClickListener)
// Failure
val showFailure = chipState is TransferFailed
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
if (showFailure) { View.VISIBLE } else { View.GONE }
// Future handling
if (chipState is TransferInitiated) {
addFutureCallback(chipState)
@@ -101,9 +106,14 @@ class MediaTttChipControllerSender @Inject constructor(
)
}
} catch (ex: Exception) {
// TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
mainExecutor.execute {
removeChip()
displayChip(
TransferFailed(
chipState.appIconDrawable,
chipState.appIconContentDescription,
chipState.otherDeviceName,
)
)
}
}
}

View File

@@ -43,6 +43,18 @@ class MediaTttSenderService @Inject constructor(
) {
this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
}
override fun closeToReceiverToEndCast(
mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
) {
this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
}
override fun transferFailed(
mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
) {
this@MediaTttSenderService.transferFailed(mediaInfo, otherDeviceInfo)
}
}
// TODO(b/203800643): Use the app icon from the media info instead of a fake one.
@@ -63,4 +75,22 @@ class MediaTttSenderService @Inject constructor(
)
controller.displayChip(chipState)
}
private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
val chipState = MoveCloserToEndCast(
appIconDrawable = fakeAppIconDrawable,
appIconContentDescription = mediaInfo.name.toString(),
otherDeviceName = otherDeviceInfo.name
)
controller.displayChip(chipState)
}
private fun transferFailed(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
val chipState = TransferFailed(
appIconDrawable = fakeAppIconDrawable,
appIconContentDescription = mediaInfo.name.toString(),
otherDeviceName = otherDeviceInfo.name
)
controller.displayChip(chipState)
}
}

View File

@@ -126,6 +126,17 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_moveCloserToEndCast_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getMoveCloserToEndCastCommand())
assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
verify(mediaSenderService).closeToReceiverToEndCast(any(), capture(deviceInfoCaptor))
assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_transferInitiated_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
@@ -140,6 +151,14 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
verify(mediaTttChipControllerSender).displayChip(any(TransferSucceeded::class.java))
}
@Test
fun sender_transferFailed_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getTransferFailedCommand())
assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
verify(mediaSenderService).transferFailed(any(), any())
}
@Test
fun sender_removeCommand_chipRemoved() {
commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_SENDER_TAG))
@@ -168,6 +187,13 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
)
private fun getMoveCloserToEndCastCommand(): Array<String> =
arrayOf(
ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
MOVE_CLOSER_TO_END_CAST_COMMAND_NAME
)
private fun getTransferInitiatedCommand(): Array<String> =
arrayOf(
ADD_CHIP_COMMAND_SENDER_TAG,
@@ -182,6 +208,13 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
TRANSFER_SUCCEEDED_COMMAND_NAME
)
private fun getTransferFailedCommand(): Array<String> =
arrayOf(
ADD_CHIP_COMMAND_SENDER_TAG,
DEVICE_NAME,
TRANSFER_FAILED_COMMAND_NAME
)
class EmptyCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
}

View File

@@ -66,7 +66,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
@Test
fun moveCloserToStartCast_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
fun moveCloserToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
controllerSender.displayChip(moveCloserToStartCast())
val chipView = getChipView()
@@ -75,10 +75,24 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo() {
fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
controllerSender.displayChip(moveCloserToEndCast())
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
fun transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo_noFailureIcon() {
val future: SettableFuture<Runnable?> = SettableFuture.create()
controllerSender.displayChip(transferInitiated(future))
@@ -91,6 +105,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
@@ -116,7 +131,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
@Test
fun transferInitiated_futureCancelled_chipRemoved() {
fun transferInitiated_futureCancelled_switchesToTransferFailed() {
val future: SettableFuture<Runnable?> = SettableFuture.create()
controllerSender.displayChip(transferInitiated(future))
@@ -129,12 +144,15 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
// Assert we ran the future callback
assertThat(numRun).isEqualTo(1)
// Assert that we've hidden the chip
verify(windowManager).removeView(any())
// Assert that we've moved to the failed state
val chipView = getChipView()
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
fun transferInitiated_futureNotResolvedAfterTimeout_switchesToTransferFailed() {
val future: SettableFuture<Runnable?> = SettableFuture.create()
controllerSender.displayChip(transferInitiated(future))
@@ -148,12 +166,15 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
// Assert we eventually decide to not wait for the future anymore
assertThat(numRun).isEqualTo(1)
// Assert we've hidden the chip
verify(windowManager).removeView(any())
// Assert that we've moved to the failed state
val chipView = getChipView()
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun transferSucceeded_appIcon_chipTextContainsDeviceName_noLoadingIcon() {
fun transferSucceeded_appIcon_deviceName_noLoadingIcon_noFailureIcon() {
controllerSender.displayChip(transferSucceeded())
val chipView = getChipView()
@@ -161,6 +182,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
}
@Test
@@ -191,6 +213,19 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(runnableRun).isTrue()
}
@Test
fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
controllerSender.displayChip(transferFailed())
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_ICON_CONTENT_DESC)
assertThat(chipView.getChipText()).doesNotContain(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromCloserToStartToTransferInitiated_loadingIconAppears() {
controllerSender.displayChip(moveCloserToStartCast())
@@ -223,6 +258,14 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@Test
fun changeFromTransferInitiatedToTransferFailed_failureIconAppears() {
controllerSender.displayChip(transferInitiated())
controllerSender.displayChip(transferFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
@@ -233,6 +276,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private fun LinearLayout.getUndoButton(): View = this.requireViewById(R.id.undo)
private fun LinearLayout.getFailureIcon(): View = this.requireViewById(R.id.failure_icon)
private fun getChipView(): LinearLayout {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
@@ -243,6 +288,10 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private fun moveCloserToStartCast() =
MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun moveCloserToEndCast() =
MoveCloserToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferInitiated(
future: Future<Runnable?> = TEST_FUTURE
@@ -252,6 +301,10 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
private fun transferSucceeded(
undoRunnable: Runnable? = null
) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferFailed() =
TransferFailed(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
}
private const val DEVICE_NAME = "My Tablet"

View File

@@ -5,6 +5,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.shared.mediattt.DeviceInfo
import com.android.systemui.shared.mediattt.IDeviceSenderCallback
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.google.common.truth.Truth.assertThat
@@ -45,4 +46,23 @@ class MediaTttSenderServiceTest : SysuiTestCase() {
val chipState = chipStateCaptor.value!!
assertThat(chipState.otherDeviceName).isEqualTo(name)
}
@Test
fun closeToReceiverToEndCast_controllerTriggeredWithMoveCloserToEndCastState() {
val name = "Fake name"
callback.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name))
val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>()
verify(controller).displayChip(capture(chipStateCaptor))
val chipState = chipStateCaptor.value!!
assertThat(chipState.otherDeviceName).isEqualTo(name)
}
@Test
fun transferFailed_controllerTriggeredWithTransferFailedState() {
callback.transferFailed(mediaInfo, DeviceInfo("Fake name"))
verify(controller).displayChip(any<TransferFailed>())
}
}