[Media TTT] Add error state.
Screenshot of chip is in the linked bug. Fixes: 211493953 Test: manual: verify `adb shell cmd statusbar media-ttt-chip-add-sender Tablet TransferFailed` triggers failed chip Test: media.taptotransfer tests Change-Id: Ic4074394af1bf619581bad479eb6828ff04368d1
This commit is contained in:
19
packages/SystemUI/res/drawable/ic_warning.xml
Normal file
19
packages/SystemUI/res/drawable/ic_warning.xml
Normal 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>
|
||||
@@ -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"
|
||||
|
||||
@@ -980,10 +980,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>
|
||||
|
||||
@@ -2193,6 +2193,8 @@
|
||||
<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>
|
||||
|
||||
@@ -59,4 +59,12 @@ interface IDeviceSenderCallback {
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSen
|
||||
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
|
||||
@@ -122,12 +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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -238,6 +245,8 @@ val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName
|
||||
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"
|
||||
|
||||
@@ -107,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
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,12 @@ class MediaTttSenderService @Inject constructor(
|
||||
) {
|
||||
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.
|
||||
@@ -78,4 +84,13 @@ class MediaTttSenderService @Inject constructor(
|
||||
)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,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))
|
||||
@@ -200,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>) {
|
||||
}
|
||||
|
||||
@@ -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,11 @@ 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 moveCloserToEndCast_appIcon_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
|
||||
fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
|
||||
controllerSender.displayChip(moveCloserToEndCast())
|
||||
|
||||
val chipView = getChipView()
|
||||
@@ -87,10 +88,11 @@ 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 transferInitiated_futureNotResolvedYet_appIcon_loadingIcon_noUndo_noFailureIcon() {
|
||||
val future: SettableFuture<Runnable?> = SettableFuture.create()
|
||||
controllerSender.displayChip(transferInitiated(future))
|
||||
|
||||
@@ -103,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
|
||||
@@ -128,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))
|
||||
@@ -141,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))
|
||||
|
||||
@@ -160,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()
|
||||
@@ -173,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
|
||||
@@ -203,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())
|
||||
@@ -235,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 =
|
||||
@@ -245,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())
|
||||
@@ -268,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"
|
||||
|
||||
@@ -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
|
||||
@@ -57,4 +58,11 @@ class MediaTttSenderServiceTest : SysuiTestCase() {
|
||||
val chipState = chipStateCaptor.value!!
|
||||
assertThat(chipState.otherDeviceName).isEqualTo(name)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferFailed_controllerTriggeredWithTransferFailedState() {
|
||||
callback.transferFailed(mediaInfo, DeviceInfo("Fake name"))
|
||||
|
||||
verify(controller).displayChip(any<TransferFailed>())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user