[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:
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -93,7 +93,10 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
mainExecutor.execute {
|
||||
displayChip(
|
||||
TransferSucceeded(
|
||||
chipState.appIconDrawable, chipState.otherDeviceName, undoRunnable
|
||||
chipState.appIconDrawable,
|
||||
chipState.appIconContentDescription,
|
||||
chipState.otherDeviceName,
|
||||
undoRunnable
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user