[Media TTT] Add content description to app icon.

Fixes: 211661947
Test: manual with talkback
Test: media.taptotransfer test suite
Change-Id: I453ce134b94a4016e630adee88329b4d2bf869f4
This commit is contained in:
Caitlin Cassidy
2021-12-22 16:58:42 +00:00
parent 05898447fc
commit ea3174a687
9 changed files with 102 additions and 29 deletions

View File

@@ -71,20 +71,32 @@ class MediaTttCommandLineHelper @Inject constructor(
when (args[1]) {
MOVE_CLOSER_TO_TRANSFER_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
MoveCloserToTransfer(appIconDrawable, otherDeviceName)
MoveCloserToTransfer(
appIconDrawable, APP_ICON_CONTENT_DESCRIPTION, otherDeviceName
)
)
}
TRANSFER_INITIATED_COMMAND_NAME -> {
val futureTask = FutureTask { fakeUndoRunnable }
mediaTttChipControllerSender.displayChip(
TransferInitiated(appIconDrawable, otherDeviceName, futureTask)
TransferInitiated(
appIconDrawable,
APP_ICON_CONTENT_DESCRIPTION,
otherDeviceName,
futureTask
)
)
mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
mediaTttChipControllerSender.displayChip(
TransferSucceeded(appIconDrawable, otherDeviceName, fakeUndoRunnable)
TransferSucceeded(
appIconDrawable,
APP_ICON_CONTENT_DESCRIPTION,
otherDeviceName,
fakeUndoRunnable
)
)
}
else -> {
@@ -118,7 +130,9 @@ class MediaTttCommandLineHelper @Inject constructor(
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
mediaTttChipControllerReceiver.displayChip(ChipStateReceiver(appIconDrawable))
mediaTttChipControllerReceiver.displayChip(
ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
)
}
override fun help(pw: PrintWriter) {
pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_RECEIVER_TAG")
@@ -156,4 +170,5 @@ val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
private const val FUTURE_WAIT_TIME = 2000L
private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
private const val TAG = "MediaTapToTransferCli"

View File

@@ -99,6 +99,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
internal fun setIcon(chipState: T, currentChipView: ViewGroup) {
currentChipView.findViewById<CachingIconView>(R.id.app_icon).apply {
this.setImageDrawable(chipState.appIconDrawable)
this.contentDescription = chipState.appIconContentDescription
}
}
}

View File

@@ -22,7 +22,9 @@ import android.graphics.drawable.Drawable
* A superclass chip state that will be subclassed by the sender chip and receiver chip.
*
* @property appIconDrawable a drawable representing the icon of the app playing the media.
* @property appIconContentDescription a string to use as the content description for the icon.
*/
open class MediaTttChipState(
internal val appIconDrawable: Drawable
internal val appIconDrawable: Drawable,
internal val appIconContentDescription: String
)

View File

@@ -24,5 +24,6 @@ import com.android.systemui.media.taptotransfer.common.MediaTttChipState
* the receiver device.
*/
class ChipStateReceiver(
appIconDrawable: Drawable
) : MediaTttChipState(appIconDrawable)
appIconDrawable: Drawable,
appIconContentDescription: String
) : MediaTttChipState(appIconDrawable, appIconContentDescription)

View File

@@ -34,9 +34,10 @@ import java.util.concurrent.Future
*/
sealed class ChipStateSender(
appIconDrawable: Drawable,
appIconContentDescription: String,
@StringRes internal val chipText: Int,
internal val otherDeviceName: String,
) : MediaTttChipState(appIconDrawable)
) : MediaTttChipState(appIconDrawable, appIconContentDescription)
/**
* A state representing that the two devices are close but not close enough to initiate a transfer.
@@ -44,8 +45,14 @@ sealed class ChipStateSender(
*/
class MoveCloserToTransfer(
appIconDrawable: Drawable,
appIconContentDescription: String,
otherDeviceName: String,
) : ChipStateSender(appIconDrawable, R.string.media_move_closer_to_transfer, otherDeviceName)
) : ChipStateSender(
appIconDrawable,
appIconContentDescription,
R.string.media_move_closer_to_transfer,
otherDeviceName
)
/**
* A state representing that a transfer has been initiated (but not completed).
@@ -57,9 +64,15 @@ class MoveCloserToTransfer(
*/
class TransferInitiated(
appIconDrawable: Drawable,
appIconContentDescription: String,
otherDeviceName: String,
val future: Future<Runnable?>
) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
) : ChipStateSender(
appIconDrawable,
appIconContentDescription,
R.string.media_transfer_playing,
otherDeviceName
)
/**
* A state representing that a transfer has been successfully completed.
@@ -69,6 +82,11 @@ class TransferInitiated(
*/
class TransferSucceeded(
appIconDrawable: Drawable,
appIconContentDescription: String,
otherDeviceName: String,
val undoRunnable: Runnable? = null
) : ChipStateSender(appIconDrawable, R.string.media_transfer_playing, otherDeviceName)
) : ChipStateSender(appIconDrawable,
appIconContentDescription,
R.string.media_transfer_playing,
otherDeviceName
)

View File

@@ -93,7 +93,10 @@ class MediaTttChipControllerSender @Inject constructor(
mainExecutor.execute {
displayChip(
TransferSucceeded(
chipState.appIconDrawable, chipState.otherDeviceName, undoRunnable
chipState.appIconDrawable,
chipState.appIconContentDescription,
chipState.otherDeviceName,
undoRunnable
)
)
}

View File

@@ -19,14 +19,18 @@ package com.android.systemui.media.taptotransfer.common
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -50,24 +54,24 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Test
fun displayChip_chipAdded() {
controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
controllerCommon.displayChip(getState())
verify(windowManager).addView(any(), any())
}
@Test
fun displayChip_twice_chipNotAddedTwice() {
controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
controllerCommon.displayChip(getState())
reset(windowManager)
controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
controllerCommon.displayChip(getState())
verify(windowManager, never()).addView(any(), any())
}
@Test
fun removeChip_chipRemoved() {
// First, add the chip
controllerCommon.displayChip(MediaTttChipState(appIconDrawable))
controllerCommon.displayChip(getState())
// Then, remove it
controllerCommon.removeChip()
@@ -82,6 +86,29 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
}
@Test
fun setIcon_viewHasIconAndContentDescription() {
controllerCommon.displayChip(getState())
val chipView = getChipView()
val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
val contentDescription = "test description"
controllerCommon.setIcon(MediaTttChipState(drawable, contentDescription), chipView)
assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(contentDescription)
}
private fun getState() = MediaTttChipState(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup
}
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
inner class TestControllerCommon(
context: Context,
windowManager: WindowManager
@@ -92,3 +119,5 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
}
}
}
private const val APP_ICON_CONTENT_DESCRIPTION = "Content description"

View File

@@ -16,7 +16,6 @@
package com.android.systemui.media.taptotransfer.receiver
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.view.View
import android.view.ViewGroup
@@ -50,10 +49,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Test
fun displayChip_chipContainsIcon() {
val drawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
val contentDescription = "Test description"
controllerReceiver.displayChip(ChipStateReceiver(drawable))
controllerReceiver.displayChip(ChipStateReceiver(drawable, contentDescription))
assertThat(getChipView().getAppIconDrawable()).isEqualTo(drawable)
assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(contentDescription)
}
private fun getChipView(): ViewGroup {
@@ -62,6 +63,5 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
return viewCaptor.value as ViewGroup
}
private fun ViewGroup.getAppIconDrawable(): Drawable =
(this.requireViewById<ImageView>(R.id.app_icon)).drawable
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}

View File

@@ -70,7 +70,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
controllerSender.displayChip(moveCloserToTransfer())
val chipView = getChipView()
assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
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)
@@ -85,7 +86,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
// Assert we're still in the loading state
val chipView = getChipView()
assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
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.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
@@ -155,7 +157,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
controllerSender.displayChip(transferSucceeded())
val chipView = getChipView()
assertThat(chipView.getAppIconDrawable()).isEqualTo(appIconDrawable)
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)
}
@@ -220,8 +223,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
private fun LinearLayout.getAppIconDrawable(): Drawable =
(this.requireViewById<ImageView>(R.id.app_icon)).drawable
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun LinearLayout.getChipText(): String =
(this.requireViewById<TextView>(R.id.text)).text as String
@@ -238,20 +240,22 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
}
/** Helper method providing default parameters to not clutter up the tests. */
private fun moveCloserToTransfer() = MoveCloserToTransfer(appIconDrawable, DEVICE_NAME)
private fun moveCloserToTransfer() =
MoveCloserToTransfer(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
) = TransferInitiated(appIconDrawable, DEVICE_NAME, future)
) = TransferInitiated(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, future)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferSucceeded(
undoRunnable: Runnable? = null
) = TransferSucceeded(appIconDrawable, DEVICE_NAME, undoRunnable)
) = TransferSucceeded(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoRunnable)
}
private const val DEVICE_NAME = "My Tablet"
private const val APP_ICON_CONTENT_DESC = "Content description"
// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
// state.
private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()