Merge "[Media TTT] Refactor media states to use an enum instead of sealed classes." into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-07 12:47:50 +00:00
committed by Android (Google) Code Review
12 changed files with 585 additions and 536 deletions

View File

@@ -16,6 +16,7 @@
package com.android.systemui.media.taptotransfer package com.android.systemui.media.taptotransfer
import android.annotation.SuppressLint
import android.app.StatusBarManager import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.media.MediaRoute2Info import android.media.MediaRoute2Info
@@ -23,16 +24,12 @@ import android.util.Log
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter import java.io.PrintWriter
import java.lang.IllegalArgumentException
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
@@ -46,28 +43,6 @@ class MediaTttCommandLineHelper @Inject constructor(
private val context: Context, private val context: Context,
@Main private val mainExecutor: Executor @Main private val mainExecutor: Executor
) { ) {
/**
* A map from a display state string typed in the command line to the display int it represents.
*/
private val stateStringToStateInt: Map<String, Int> = mapOf(
AlmostCloseToStartCast::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
AlmostCloseToEndCast::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
TransferToReceiverTriggered::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
TransferToThisDeviceTriggered::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
TransferToReceiverSucceeded::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
TransferToThisDeviceSucceeded::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
TransferFailed::class.simpleName!!
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
FAR_FROM_RECEIVER_STATE
to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER
)
init { init {
commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() } commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() } commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
@@ -76,21 +51,23 @@ class MediaTttCommandLineHelper @Inject constructor(
/** All commands for the sender device. */ /** All commands for the sender device. */
inner class SenderCommand : Command { inner class SenderCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) { override fun execute(pw: PrintWriter, args: List<String>) {
val routeInfo = MediaRoute2Info.Builder("id", args[0])
.addFeature("feature")
.setPackageName(TEST_PACKAGE_NAME)
.build()
val commandName = args[1] val commandName = args[1]
@StatusBarManager.MediaTransferSenderState @StatusBarManager.MediaTransferSenderState
val displayState = stateStringToStateInt[commandName] val displayState: Int?
if (displayState == null) { try {
displayState = ChipStateSender.getSenderStateIdFromName(commandName)
} catch (ex: IllegalArgumentException) {
pw.println("Invalid command name $commandName") pw.println("Invalid command name $commandName")
return return
} }
@SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager as StatusBarManager
val routeInfo = MediaRoute2Info.Builder("id", args[0])
.addFeature("feature")
.setPackageName(TEST_PACKAGE_NAME)
.build()
statusBarManager.updateMediaTapToTransferSenderDisplay( statusBarManager.updateMediaTapToTransferSenderDisplay(
displayState, displayState,
routeInfo, routeInfo,
@@ -136,6 +113,17 @@ class MediaTttCommandLineHelper @Inject constructor(
/** All commands for the receiver device. */ /** All commands for the receiver device. */
inner class ReceiverCommand : Command { inner class ReceiverCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) { override fun execute(pw: PrintWriter, args: List<String>) {
val commandName = args[0]
@StatusBarManager.MediaTransferReceiverState
val displayState: Int?
try {
displayState = ChipStateReceiver.getReceiverStateIdFromName(commandName)
} catch (ex: IllegalArgumentException) {
pw.println("Invalid command name $commandName")
return
}
@SuppressLint("WrongConstant") // sysui is allowed to call STATUS_BAR_SERVICE
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE) val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
as StatusBarManager as StatusBarManager
val routeInfo = MediaRoute2Info.Builder("id", "Test Name") val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
@@ -143,24 +131,12 @@ class MediaTttCommandLineHelper @Inject constructor(
.setPackageName(TEST_PACKAGE_NAME) .setPackageName(TEST_PACKAGE_NAME)
.build() .build()
when(val commandName = args[0]) { statusBarManager.updateMediaTapToTransferReceiverDisplay(
CLOSE_TO_SENDER_STATE -> displayState,
statusBarManager.updateMediaTapToTransferReceiverDisplay( routeInfo,
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, null,
routeInfo, null
null, )
null
)
FAR_FROM_SENDER_STATE ->
statusBarManager.updateMediaTapToTransferReceiverDisplay(
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
routeInfo,
null,
null
)
else ->
pw.println("Invalid command name $commandName")
}
} }
override fun help(pw: PrintWriter) { override fun help(pw: PrintWriter) {
@@ -173,11 +149,5 @@ class MediaTttCommandLineHelper @Inject constructor(
const val SENDER_COMMAND = "media-ttt-chip-sender" const val SENDER_COMMAND = "media-ttt-chip-sender"
@VisibleForTesting @VisibleForTesting
const val RECEIVER_COMMAND = "media-ttt-chip-receiver" const val RECEIVER_COMMAND = "media-ttt-chip-receiver"
@VisibleForTesting
const val FAR_FROM_RECEIVER_STATE = "FarFromReceiver"
@VisibleForTesting
const val CLOSE_TO_SENDER_STATE = "CloseToSender"
@VisibleForTesting
const val FAR_FROM_SENDER_STATE = "FarFromSender"
private const val CLI_TAG = "MediaTransferCli" private const val CLI_TAG = "MediaTransferCli"
private const val TEST_PACKAGE_NAME = "com.android.systemui" private const val TEST_PACKAGE_NAME = "com.android.systemui"

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2021 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.
*/
package com.android.systemui.media.taptotransfer.common
/**
* A superclass chip state that will be subclassed by the sender chip and receiver chip.
*/
interface ChipInfoCommon {
/**
* Returns the amount of time the given chip state should display on the screen before it times
* out and disappears.
*/
fun getTimeoutMs(): Long
}
const val DEFAULT_TIMEOUT_MILLIS = 3000L

View File

@@ -19,14 +19,16 @@ package com.android.systemui.media.taptotransfer.common
import android.annotation.LayoutRes import android.annotation.LayoutRes
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.pm.PackageManager
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowManager import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView import com.android.internal.widget.CachingIconView
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
@@ -40,8 +42,11 @@ import com.android.systemui.util.view.ViewUtil
* *
* Subclasses need to override and implement [updateChipView], which is where they can control what * Subclasses need to override and implement [updateChipView], which is where they can control what
* gets displayed to the user. * gets displayed to the user.
*
* The generic type T is expected to contain all the information necessary for the subclasses to
* display the chip in a certain state, since they receive <T> in [updateChipView].
*/ */
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>( abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
internal val context: Context, internal val context: Context,
internal val logger: MediaTttLogger, internal val logger: MediaTttLogger,
private val windowManager: WindowManager, private val windowManager: WindowManager,
@@ -64,10 +69,10 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
} }
/** The chip view currently being displayed. Null if the chip is not being displayed. */ /** The chip view currently being displayed. Null if the chip is not being displayed. */
var chipView: ViewGroup? = null private var chipView: ViewGroup? = null
/** A [Runnable] that, when run, will cancel the pending timeout of the chip. */ /** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
var cancelChipViewTimeout: Runnable? = null private var cancelChipViewTimeout: Runnable? = null
/** /**
* Displays the chip with the current state. * Displays the chip with the current state.
@@ -75,7 +80,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
* This method handles inflating and attaching the view, then delegates to [updateChipView] to * This method handles inflating and attaching the view, then delegates to [updateChipView] to
* display the correct information in the chip. * display the correct information in the chip.
*/ */
fun displayChip(chipState: T) { fun displayChip(chipInfo: T) {
val oldChipView = chipView val oldChipView = chipView
if (chipView == null) { if (chipView == null) {
chipView = LayoutInflater chipView = LayoutInflater
@@ -84,7 +89,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
} }
val currentChipView = chipView!! val currentChipView = chipView!!
updateChipView(chipState, currentChipView) updateChipView(chipInfo, currentChipView)
// Add view if necessary // Add view if necessary
if (oldChipView == null) { if (oldChipView == null) {
@@ -96,7 +101,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
cancelChipViewTimeout?.run() cancelChipViewTimeout?.run()
cancelChipViewTimeout = mainExecutor.executeDelayed( cancelChipViewTimeout = mainExecutor.executeDelayed(
{ removeChip(MediaTttRemovalReason.REASON_TIMEOUT) }, { removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
chipState.getTimeoutMs() chipInfo.getTimeoutMs()
) )
} }
@@ -117,20 +122,28 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
} }
/** /**
* A method implemented by subclasses to update [currentChipView] based on [chipState]. * A method implemented by subclasses to update [currentChipView] based on [chipInfo].
*/ */
abstract fun updateChipView(chipState: T, currentChipView: ViewGroup) abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup)
/** /**
* An internal method to set the icon on the view. * An internal method to set the icon on the view.
* *
* This is in the common superclass since both the sender and the receiver show an icon. * This is in the common superclass since both the sender and the receiver show an icon.
*
* @param appPackageName the package name of the app playing the media. Will be used to fetch
* the app icon and app name if overrides aren't provided.
*/ */
internal fun setIcon(chipState: T, currentChipView: ViewGroup) { internal fun setIcon(
currentChipView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
) {
val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon) val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
appIconView.contentDescription = chipState.getAppName(context) appIconView.contentDescription = appNameOverride ?: getAppName(appPackageName)
val appIcon = chipState.getAppIcon(context) val appIcon = appIconDrawableOverride ?: getAppIcon(appPackageName)
val visibility = if (appIcon != null) { val visibility = if (appIcon != null) {
View.VISIBLE View.VISIBLE
} else { } else {
@@ -140,6 +153,30 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
appIconView.visibility = visibility appIconView.visibility = visibility
} }
/** Returns the icon of the app playing the media or null if we can't find it. */
private fun getAppIcon(appPackageName: String?): Drawable? {
appPackageName ?: return null
return try {
context.packageManager.getApplicationIcon(appPackageName)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "Cannot find icon for package $appPackageName", e)
null
}
}
/** Returns the name of the app playing the media or null if we can't find it. */
private fun getAppName(appPackageName: String?): String? {
appPackageName ?: return null
return try {
context.packageManager.getApplicationInfo(
appPackageName, PackageManager.ApplicationInfoFlags.of(0)
).loadLabel(context.packageManager).toString()
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "Cannot find name for package $appPackageName", e)
null
}
}
private fun onScreenTapped(e: MotionEvent) { private fun onScreenTapped(e: MotionEvent) {
val view = chipView ?: return val view = chipView ?: return
// If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the // If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the
@@ -159,4 +196,3 @@ object MediaTttRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT" const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP" const val REASON_SCREEN_TAP = "SCREEN_TAP"
} }

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2021 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.
*/
package com.android.systemui.media.taptotransfer.common
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.util.Log
/**
* A superclass chip state that will be subclassed by the sender chip and receiver chip.
*
* @property appPackageName the package name of the app playing the media. Will be used to fetch the
* app icon and app name.
*/
open class MediaTttChipState(
internal val appPackageName: String?,
) {
open fun getAppIcon(context: Context): Drawable? {
appPackageName ?: return null
return try {
context.packageManager.getApplicationIcon(appPackageName)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "Cannot find icon for package $appPackageName", e)
null
}
}
/** Returns the name of the app playing the media or null if we can't find it. */
open fun getAppName(context: Context): String? {
appPackageName ?: return null
return try {
context.packageManager.getApplicationInfo(
appPackageName, PackageManager.ApplicationInfoFlags.of(0)
).loadLabel(context.packageManager).toString()
} catch (e: PackageManager.NameNotFoundException) {
Log.w(TAG, "Cannot find name for package $appPackageName", e)
null
}
}
/**
* Returns the amount of time this chip should display on the screen before it times out and
* disappears. [MediaTttChipControllerCommon] will ensure that the timeout resets each time we
* receive a new state.
*/
open fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
}
private const val DEFAULT_TIMEOUT_MILLIS = 3000L
private val TAG = MediaTttChipState::class.simpleName!!

View File

@@ -16,35 +16,41 @@
package com.android.systemui.media.taptotransfer.receiver package com.android.systemui.media.taptotransfer.receiver
import android.content.Context import android.app.StatusBarManager
import android.graphics.drawable.Drawable import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
import com.android.systemui.media.taptotransfer.common.MediaTttChipState import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
/** /**
* A class that stores all the information necessary to display the media tap-to-transfer chip on * A class that stores all the information necessary to display the media tap-to-transfer chip on
* the receiver device. * the receiver device.
*
* @property appIconDrawable a drawable representing the icon of the app playing the media. If
* present, this will be used in [this.getAppIcon] instead of [appPackageName].
* @property appName a name for the app playing the media. If present, this will be used in
* [this.getAppName] instead of [appPackageName].
*/ */
class ChipStateReceiver( enum class ChipStateReceiver(
appPackageName: String?, @StatusBarManager.MediaTransferSenderState val stateInt: Int,
private val appIconDrawable: Drawable?, ) {
private val appName: CharSequence? CLOSE_TO_SENDER(
) : MediaTttChipState(appPackageName) { StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
override fun getAppIcon(context: Context): Drawable? { ),
if (appIconDrawable != null) { FAR_FROM_SENDER(
return appIconDrawable StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
} );
return super.getAppIcon(context)
}
override fun getAppName(context: Context): String? { companion object {
if (appName != null) { /**
return appName.toString() * Returns the receiver state enum associated with the given [displayState] from
} * [StatusBarManager].
return super.getAppName(context) */
fun getReceiverStateFromId(
@StatusBarManager.MediaTransferReceiverState displayState: Int
) : ChipStateReceiver = values().first { it.stateInt == displayState }
/**
* Returns the state int from [StatusBarManager] associated with the given sender state
* name.
*
* @param name the name of one of the [ChipStateReceiver] enums.
*/
@StatusBarManager.MediaTransferReceiverState
fun getReceiverStateIdFromName(name: String): Int = valueOf(name).stateInt
} }
} }

View File

@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver
import android.app.StatusBarManager import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.media.MediaRoute2Info import android.media.MediaRoute2Info
import android.os.Handler import android.os.Handler
@@ -27,6 +28,8 @@ import android.view.WindowManager
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
@@ -50,7 +53,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
mainExecutor: DelayableExecutor, mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector, tapGestureDetector: TapGestureDetector,
@Main private val mainHandler: Handler, @Main private val mainHandler: Handler,
) : MediaTttChipControllerCommon<ChipStateReceiver>( ) : MediaTttChipControllerCommon<ChipReceiverInfo>(
context, context,
logger, logger,
windowManager, windowManager,
@@ -82,45 +85,50 @@ class MediaTttChipControllerReceiver @Inject constructor(
appIcon: Icon?, appIcon: Icon?,
appName: CharSequence? appName: CharSequence?
) { ) {
logger.logStateChange(stateIntToString(displayState), routeInfo.id) val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
when(displayState) { val stateName = chipState?.name ?: "Invalid"
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> { logger.logStateChange(stateName, routeInfo.id)
val packageName = routeInfo.packageName
if (appIcon == null) { if (chipState == null) {
displayChip(ChipStateReceiver(packageName, null, appName)) Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
} else { return
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
displayChip(
ChipStateReceiver(packageName, drawable, appName)
)},
// Notify the listener on the main handler since the listener will update
// the UI.
mainHandler
)
}
}
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER ->
removeChip(removalReason = FAR_FROM_SENDER)
else ->
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
} }
if (chipState == ChipStateReceiver.FAR_FROM_SENDER) {
removeChip(removalReason = ChipStateReceiver.FAR_FROM_SENDER::class.simpleName!!)
return
}
if (appIcon == null) {
displayChip(ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appName))
return
}
appIcon.loadDrawableAsync(
context,
Icon.OnDrawableLoadedListener { drawable ->
displayChip(ChipReceiverInfo(routeInfo, drawable, appName))
},
// Notify the listener on the main handler since the listener will update
// the UI.
mainHandler
)
} }
override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) { override fun updateChipView(chipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
setIcon(chipState, currentChipView) setIcon(
} currentChipView,
chipInfo.routeInfo.packageName,
private fun stateIntToString(@StatusBarManager.MediaTransferReceiverState state: Int): String { chipInfo.appIconDrawableOverride,
return when (state) { chipInfo.appNameOverride
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> CLOSE_TO_SENDER )
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> FAR_FROM_SENDER
else -> "INVALID: $state"
}
} }
} }
data class ChipReceiverInfo(
val routeInfo: MediaRoute2Info,
val appIconDrawableOverride: Drawable?,
val appNameOverride: CharSequence?
) : ChipInfoCommon {
override fun getTimeoutMs() = DEFAULT_TIMEOUT_MILLIS
}
private const val RECEIVER_TAG = "MediaTapToTransferRcvr" private const val RECEIVER_TAG = "MediaTapToTransferRcvr"
private const val CLOSE_TO_SENDER = "CLOSE_TO_SENDER"
private const val FAR_FROM_SENDER = "FAR_FROM_SENDER"

View File

@@ -16,27 +16,168 @@
package com.android.systemui.media.taptotransfer.sender package com.android.systemui.media.taptotransfer.sender
import android.app.StatusBarManager
import android.content.Context import android.content.Context
import android.media.MediaRoute2Info
import android.view.View import android.view.View
import androidx.annotation.StringRes
import com.android.internal.statusbar.IUndoMediaTransferCallback import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.media.taptotransfer.common.MediaTttChipState import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
/** /**
* A class that stores all the information necessary to display the media tap-to-transfer chip on * A class enumerating all the possible states of the media tap-to-transfer chip on the sender
* the sender device. * device.
* *
* This is a sealed class where each subclass represents a specific chip state. Each subclass can * @property stateInt the integer from [StatusBarManager] corresponding with this state.
* contain additional information that is necessary for only that state. * @property stringResId the res ID of the string that should be displayed in the chip. Null if the
* state should not have the chip be displayed.
* @property isMidTransfer true if the state represents that a transfer is currently ongoing.
* @property isTransferFailure true if the state represents that the transfer has failed.
* @property timeout the amount of time this chip should display on the screen before it times out
* and disappears.
*/ */
sealed class ChipStateSender( enum class ChipStateSender(
appPackageName: String? @StatusBarManager.MediaTransferSenderState val stateInt: Int,
) : MediaTttChipState(appPackageName) { @StringRes val stringResId: Int?,
/** Returns a fully-formed string with the text that the chip should display. */ val isMidTransfer: Boolean = false,
abstract fun getChipTextString(context: Context): String val isTransferFailure: Boolean = false,
val timeout: Long = DEFAULT_TIMEOUT_MILLIS
) {
/**
* A state representing that the two devices are close but not close enough to *start* a cast to
* the receiver device. The chip will instruct the user to move closer in order to initiate the
* transfer to the receiver.
*/
ALMOST_CLOSE_TO_START_CAST(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
R.string.media_move_closer_to_start_cast,
),
/** Returns true if the loading icon should be displayed and false otherwise. */ /**
open fun showLoading(): Boolean = false * 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).
*/
ALMOST_CLOSE_TO_END_CAST(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
R.string.media_move_closer_to_end_cast,
),
/**
* A state representing that a transfer to the receiver device has been initiated (but not
* completed).
*/
TRANSFER_TO_RECEIVER_TRIGGERED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
R.string.media_transfer_playing_different_device,
isMidTransfer = true,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
),
/**
* A state representing that a transfer from the receiver device and back to this device (the
* sender) has been initiated (but not completed).
*/
TRANSFER_TO_THIS_DEVICE_TRIGGERED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
R.string.media_transfer_playing_this_device,
isMidTransfer = true,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
),
/**
* A state representing that a transfer to the receiver device has been successfully completed.
*/
TRANSFER_TO_RECEIVER_SUCCEEDED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
R.string.media_transfer_playing_different_device
) {
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToThisDeviceTriggered
// state, but that may take too long to go through the binder and the user may be
// confused ast o why the UI hasn't changed yet. So, we immediately change the UI
// here.
controllerSender.displayChip(
ChipSenderInfo(
TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo, undoCallback
)
)
}
}
},
/**
* A state representing that a transfer back to this device has been successfully completed.
*/
TRANSFER_TO_THIS_DEVICE_SUCCEEDED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
R.string.media_transfer_playing_this_device
) {
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?,
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToReceiverTriggered
// state, but that may take too long to go through the binder and the user may be
// confused as to why the UI hasn't changed yet. So, we immediately change the UI
// here.
controllerSender.displayChip(
ChipSenderInfo(
TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo, undoCallback
)
)
}
}
},
/** A state representing that a transfer to the receiver device has failed. */
TRANSFER_TO_RECEIVER_FAILED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
R.string.media_transfer_failed,
isTransferFailure = true
),
/** A state representing that a transfer back to this device has failed. */
TRANSFER_TO_THIS_DEVICE_FAILED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
R.string.media_transfer_failed,
isTransferFailure = true
),
/** A state representing that this device is far away from any receiver device. */
FAR_FROM_RECEIVER(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
stringResId = null
);
/**
* Returns a fully-formed string with the text that the chip should display.
*
* @param otherDeviceName the name of the other device involved in the transfer.
*/
fun getChipTextString(context: Context, otherDeviceName: String): String? {
if (stringResId == null) {
return null
}
return context.getString(stringResId, otherDeviceName)
}
/** /**
* Returns a click listener for the undo button on the chip. Returns null if this chip state * Returns a click listener for the undo button on the chip. Returns null if this chip state
@@ -44,160 +185,36 @@ sealed class ChipStateSender(
* *
* @param controllerSender passed as a parameter in case we want to display a new chip state * @param controllerSender passed as a parameter in case we want to display a new chip state
* when undo is clicked. * when undo is clicked.
* @param undoCallback if present, the callback that should be called when the user clicks the
* undo button. The undo button will only be shown if this is non-null.
*/ */
open fun undoClickListener( open fun undoClickListener(
controllerSender: MediaTttChipControllerSender controllerSender: MediaTttChipControllerSender,
routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback?
): View.OnClickListener? = null ): View.OnClickListener? = null
}
/** companion object {
* A state representing that the two devices are close but not close enough to *start* a cast to /**
* the receiver device. The chip will instruct the user to move closer in order to initiate the * Returns the sender state enum associated with the given [displayState] from
* transfer to the receiver. * [StatusBarManager].
* */
* @property otherDeviceName the name of the other device involved in the transfer. fun getSenderStateFromId(
*/ @StatusBarManager.MediaTransferSenderState displayState: Int,
class AlmostCloseToStartCast( ): ChipStateSender = values().first { it.stateInt == displayState }
appPackageName: String?,
private val otherDeviceName: String,
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_move_closer_to_start_cast, otherDeviceName)
}
}
/** /**
* A state representing that the two devices are close but not close enough to *end* a cast that's * Returns the state int from [StatusBarManager] associated with the given sender state
* currently occurring the receiver device. The chip will instruct the user to move closer in order * name.
* to initiate the transfer from the receiver and back onto this device (the original sender). *
* * @param name the name of one of the [ChipStateSender] enums.
* @property otherDeviceName the name of the other device involved in the transfer. */
*/ @StatusBarManager.MediaTransferSenderState
class AlmostCloseToEndCast( fun getSenderStateIdFromName(name: String): Int = valueOf(name).stateInt
appPackageName: String?,
private val otherDeviceName: String,
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_move_closer_to_end_cast, otherDeviceName)
}
}
/**
* A state representing that a transfer to the receiver device has been initiated (but not
* completed).
*
* @property otherDeviceName the name of the other device involved in the transfer.
*/
class TransferToReceiverTriggered(
appPackageName: String?,
private val otherDeviceName: String
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
}
override fun showLoading() = true
override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
}
/**
* A state representing that a transfer from the receiver device and back to this device (the
* sender) has been initiated (but not completed).
*/
class TransferToThisDeviceTriggered(
appPackageName: String?,
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_this_device)
}
override fun showLoading() = true
override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
}
/**
* A state representing that a transfer to the receiver device has been successfully completed.
*
* @property otherDeviceName the name of the other device involved in the transfer.
* @property undoCallback if present, the callback that should be called when the user clicks the
* undo button. The undo button will only be shown if this is non-null.
*/
class TransferToReceiverSucceeded(
appPackageName: String?,
private val otherDeviceName: String,
val undoCallback: IUndoMediaTransferCallback? = null
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
}
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
this.undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToThisDeviceTriggered state,
// but that may take too long to go through the binder and the user may be confused as
// to why the UI hasn't changed yet. So, we immediately change the UI here.
controllerSender.displayChip(
TransferToThisDeviceTriggered(this.appPackageName)
)
}
}
}
/**
* A state representing that a transfer back to this device has been successfully completed.
*
* @property otherDeviceName the name of the other device involved in the transfer.
* @property undoCallback if present, the callback that should be called when the user clicks the
* undo button. The undo button will only be shown if this is non-null.
*/
class TransferToThisDeviceSucceeded(
appPackageName: String?,
private val otherDeviceName: String,
val undoCallback: IUndoMediaTransferCallback? = null
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_this_device)
}
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender
): View.OnClickListener? {
if (undoCallback == null) {
return null
}
return View.OnClickListener {
this.undoCallback.onUndoTriggered()
// The external service should eventually send us a TransferToReceiverTriggered state,
// but that may take too long to go through the binder and the user may be confused as
// to why the UI hasn't changed yet. So, we immediately change the UI here.
controllerSender.displayChip(
TransferToReceiverTriggered(
this.appPackageName,
this.otherDeviceName
)
)
}
}
}
/** A state representing that a transfer has failed. */
class TransferFailed(
appPackageName: String?,
) : ChipStateSender(appPackageName) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_failed)
} }
} }
// Give the Transfer*Triggered states a longer timeout since those states represent an active // Give the Transfer*Triggered states a longer timeout since those states represent an active
// process and we should keep the user informed about it as long as possible (but don't allow it to // process and we should keep the user informed about it as long as possible (but don't allow it to
// continue indefinitely). // continue indefinitely).
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L

View File

@@ -28,6 +28,7 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.ChipInfoCommon
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.media.taptotransfer.common.MediaTttLogger import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
@@ -50,7 +51,7 @@ class MediaTttChipControllerSender @Inject constructor(
viewUtil: ViewUtil, viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor, @Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector, tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<ChipStateSender>( ) : MediaTttChipControllerCommon<ChipSenderInfo>(
context, context,
logger, logger,
windowManager, windowManager,
@@ -82,104 +83,82 @@ class MediaTttChipControllerSender @Inject constructor(
routeInfo: MediaRoute2Info, routeInfo: MediaRoute2Info,
undoCallback: IUndoMediaTransferCallback? undoCallback: IUndoMediaTransferCallback?
) { ) {
logger.logStateChange(stateIntToString(displayState), routeInfo.id) val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
val appPackageName = routeInfo.packageName val stateName = chipState?.name ?: "Invalid"
val otherDeviceName = routeInfo.name.toString() logger.logStateChange(stateName, routeInfo.id)
val chipState = when(displayState) {
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST -> if (chipState == null) {
AlmostCloseToStartCast(appPackageName, otherDeviceName) Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST -> return
AlmostCloseToEndCast(appPackageName, otherDeviceName)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
TransferToReceiverTriggered(appPackageName, otherDeviceName)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
TransferToThisDeviceTriggered(appPackageName)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
TransferToReceiverSucceeded(appPackageName, otherDeviceName, undoCallback)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
TransferToThisDeviceSucceeded(appPackageName, otherDeviceName, undoCallback)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
TransferFailed(appPackageName)
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER -> {
removeChip(removalReason = FAR_FROM_RECEIVER)
null
}
else -> {
Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
null
}
} }
chipState?.let { if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
displayChip(it) removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
} else {
displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
} }
} }
/** Displays the chip view for the given state. */ /** Displays the chip view for the given state. */
override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) { override fun updateChipView(
chipInfo: ChipSenderInfo,
currentChipView: ViewGroup) {
val chipState = chipInfo.state
currentlyDisplayedChipState = chipState currentlyDisplayedChipState = chipState
// App icon // App icon
setIcon(chipState, currentChipView) setIcon(currentChipView, chipInfo.routeInfo.packageName)
// Text // Text
val otherDeviceName = chipInfo.routeInfo.name.toString()
currentChipView.requireViewById<TextView>(R.id.text).apply { currentChipView.requireViewById<TextView>(R.id.text).apply {
text = chipState.getChipTextString(context) text = chipState.getChipTextString(context, otherDeviceName)
} }
// Loading // Loading
currentChipView.requireViewById<View>(R.id.loading).visibility = currentChipView.requireViewById<View>(R.id.loading).visibility =
if (chipState.showLoading()) { View.VISIBLE } else { View.GONE } chipState.isMidTransfer.visibleIfTrue()
// Undo // Undo
val undoView = currentChipView.requireViewById<View>(R.id.undo) val undoView = currentChipView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(this) val undoClickListener = chipState.undoClickListener(
this, chipInfo.routeInfo, chipInfo.undoCallback
)
undoView.setOnClickListener(undoClickListener) undoView.setOnClickListener(undoClickListener)
undoView.visibility = if (undoClickListener != null) { View.VISIBLE } else { View.GONE } undoView.visibility = (undoClickListener != null).visibleIfTrue()
// Failure // Failure
val showFailure = chipState is TransferFailed
currentChipView.requireViewById<View>(R.id.failure_icon).visibility = currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
if (showFailure) { View.VISIBLE } else { View.GONE } chipState.isTransferFailure.visibleIfTrue()
} }
override fun removeChip(removalReason: String) { override fun removeChip(removalReason: String) {
// Don't remove the chip if we're mid-transfer since the user should still be able to // Don't remove the chip if we're mid-transfer since the user should still be able to
// see the status of the transfer. (But do remove it if it's finally timed out.) // see the status of the transfer. (But do remove it if it's finally timed out.)
if ((currentlyDisplayedChipState is TransferToReceiverTriggered || if (currentlyDisplayedChipState?.isMidTransfer == true
currentlyDisplayedChipState is TransferToThisDeviceTriggered) && removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
&& removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
return return
} }
super.removeChip(removalReason) super.removeChip(removalReason)
currentlyDisplayedChipState = null currentlyDisplayedChipState = null
} }
private fun stateIntToString(@StatusBarManager.MediaTransferSenderState state: Int): String { private fun Boolean.visibleIfTrue(): Int {
return when(state) { return if (this) {
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST -> View.VISIBLE
"ALMOST_CLOSE_TO_START_CAST" } else {
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST -> View.GONE
"ALMOST_CLOSE_TO_END_CAST"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
"TRANSFER_TO_RECEIVER_TRIGGERED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
"TRANSFER_TO_THIS_DEVICE_TRIGGERED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
"TRANSFER_TO_RECEIVER_SUCCEEDED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
"TRANSFER_TO_THIS_DEVICE_SUCCEEDED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED ->
"TRANSFER_TO_RECEIVER_FAILED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
"TRANSFER_TO_THIS_DEVICE_FAILED"
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER ->
FAR_FROM_RECEIVER
else -> "INVALID: $state"
} }
} }
} }
data class ChipSenderInfo(
val state: ChipStateSender,
val routeInfo: MediaRoute2Info,
val undoCallback: IUndoMediaTransferCallback? = null
) : ChipInfoCommon {
override fun getTimeoutMs() = state.timeout
}
const val SENDER_TAG = "MediaTapToTransferSender" const val SENDER_TAG = "MediaTapToTransferSender"
private const val FAR_FROM_RECEIVER = "FAR_FROM_RECEIVER"

View File

@@ -21,13 +21,8 @@ import android.content.Context
import android.media.MediaRoute2Info import android.media.MediaRoute2Info
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast import com.android.systemui.media.taptotransfer.sender.ChipStateSender
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
@@ -88,7 +83,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_almostCloseToStartCast_serviceCallbackCalled() { fun sender_almostCloseToStartCast_serviceCallbackCalled() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(AlmostCloseToStartCast::class.simpleName!!) pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
) )
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -103,7 +98,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_almostCloseToEndCast_serviceCallbackCalled() { fun sender_almostCloseToEndCast_serviceCallbackCalled() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(AlmostCloseToEndCast::class.simpleName!!) pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.name)
) )
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -118,7 +113,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() { fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(TransferToReceiverTriggered::class.simpleName!!) pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.name)
) )
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -133,7 +128,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() { fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(TransferToThisDeviceTriggered::class.simpleName!!) pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
) )
verify(statusBarManager).updateMediaTapToTransferSenderDisplay( verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
@@ -146,7 +141,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() { fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(TransferToReceiverSucceeded::class.simpleName!!) pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.name)
) )
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -161,7 +156,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() { fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
commandRegistry.onShellCommand( commandRegistry.onShellCommand(
pw, getSenderCommand(TransferToThisDeviceSucceeded::class.simpleName!!) pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.name)
) )
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>() val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
@@ -174,8 +169,10 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
} }
@Test @Test
fun sender_transferFailed_serviceCallbackCalled() { fun sender_transferToReceiverFailed_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getSenderCommand(TransferFailed::class.simpleName!!)) commandRegistry.onShellCommand(
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.name)
)
verify(statusBarManager).updateMediaTapToTransferSenderDisplay( verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED), eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED),
@@ -184,9 +181,24 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
nullable()) nullable())
} }
@Test
fun sender_transferToThisDeviceFailed_serviceCallbackCalled() {
commandRegistry.onShellCommand(
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_FAILED.name)
)
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED),
any(),
nullable(),
nullable())
}
@Test @Test
fun sender_farFromReceiver_serviceCallbackCalled() { fun sender_farFromReceiver_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getSenderCommand(FAR_FROM_RECEIVER_STATE)) commandRegistry.onShellCommand(
pw, getSenderCommand(ChipStateSender.FAR_FROM_RECEIVER.name)
)
verify(statusBarManager).updateMediaTapToTransferSenderDisplay( verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER), eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER),
@@ -197,7 +209,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun receiver_closeToSender_serviceCallbackCalled() { fun receiver_closeToSender_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE)) commandRegistry.onShellCommand(
pw, getReceiverCommand(ChipStateReceiver.CLOSE_TO_SENDER.name)
)
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER), eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
@@ -209,7 +223,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
@Test @Test
fun receiver_farFromSender_serviceCallbackCalled() { fun receiver_farFromSender_serviceCallbackCalled() {
commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE)) commandRegistry.onShellCommand(
pw, getReceiverCommand(ChipStateReceiver.FAR_FROM_SENDER.name)
)
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay( verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER), eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),

View File

@@ -17,7 +17,10 @@
package com.android.systemui.media.taptotransfer.common package com.android.systemui.media.taptotransfer.common
import android.content.Context import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.MediaRoute2Info
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@@ -27,6 +30,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.receiver.ChipReceiverInfo
import com.android.systemui.statusbar.gesture.TapGestureDetector import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.concurrency.FakeExecutor
@@ -40,6 +44,7 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.eq
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
@@ -48,12 +53,16 @@ import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
class MediaTttChipControllerCommonTest : SysuiTestCase() { class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState> private lateinit var controllerCommon: MediaTttChipControllerCommon<ChipInfo>
private lateinit var fakeClock: FakeSystemClock private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor private lateinit var fakeExecutor: FakeExecutor
private lateinit var appIconDrawable: Drawable private lateinit var appIconFromPackageName: Drawable
@Mock
private lateinit var packageManager: PackageManager
@Mock
private lateinit var applicationInfo: ApplicationInfo
@Mock @Mock
private lateinit var logger: MediaTttLogger private lateinit var logger: MediaTttLogger
@Mock @Mock
@@ -66,7 +75,15 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
whenever(packageManager.getApplicationInfo(
eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
fakeClock = FakeSystemClock() fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock) fakeExecutor = FakeExecutor(fakeClock)
@@ -100,7 +117,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(state) controllerCommon.displayChip(state)
reset(windowManager) reset(windowManager)
fakeClock.advanceTime(state.getTimeoutMs() - 1) fakeClock.advanceTime(TIMEOUT_MS - 1)
verify(windowManager, never()).removeView(any()) verify(windowManager, never()).removeView(any())
} }
@@ -111,7 +128,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(state) controllerCommon.displayChip(state)
reset(windowManager) reset(windowManager)
fakeClock.advanceTime(state.getTimeoutMs() + 1) fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any()) verify(windowManager).removeView(any())
} }
@@ -128,7 +145,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(getState()) controllerCommon.displayChip(getState())
// Wait until the timeout for the first display would've happened // Wait until the timeout for the first display would've happened
fakeClock.advanceTime(state.getTimeoutMs() - waitTime + 1) fakeClock.advanceTime(TIMEOUT_MS - waitTime + 1)
// Verify we didn't hide the chip // Verify we didn't hide the chip
verify(windowManager, never()).removeView(any()) verify(windowManager, never()).removeView(any())
@@ -145,7 +162,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
controllerCommon.displayChip(getState()) controllerCommon.displayChip(getState())
// Ensure we still hide the chip eventually // Ensure we still hide the chip eventually
fakeClock.advanceTime(state.getTimeoutMs() + 1) fakeClock.advanceTime(TIMEOUT_MS + 1)
verify(windowManager).removeView(any()) verify(windowManager).removeView(any())
} }
@@ -172,16 +189,45 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
} }
@Test @Test
fun setIcon_viewHasIconAndContentDescription() { fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
controllerCommon.displayChip(getState()) controllerCommon.displayChip(getState())
val chipView = getChipView() val chipView = getChipView()
val state = TestChipState(PACKAGE_NAME) controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
controllerCommon.setIcon(state, chipView)
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
assertThat(chipView.getAppIconView().contentDescription) }
.isEqualTo(state.getAppName(context))
@Test
fun displayChip_hasAppIconDrawable_iconIsDrawable() {
controllerCommon.displayChip(getState())
val chipView = getChipView()
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
controllerCommon.setIcon(chipView, PACKAGE_NAME, drawable, null)
assertThat(chipView.getAppIconView().drawable).isEqualTo(drawable)
}
@Test
fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
controllerCommon.displayChip(getState())
val chipView = getChipView()
controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appNameOverride = null)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
controllerCommon.displayChip(getState())
val chipView = getChipView()
val appName = "Override App Name"
controllerCommon.setIcon(chipView, PACKAGE_NAME, null, appName)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(appName)
} }
@Test @Test
@@ -218,7 +264,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any()) verify(windowManager, never()).removeView(any())
} }
private fun getState() = MediaTttChipState(PACKAGE_NAME) private fun getState() = ChipInfo()
private fun getChipView(): ViewGroup { private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java) val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -235,7 +281,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil: ViewUtil, viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor, @Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector, tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<MediaTttChipState>( ) : MediaTttChipControllerCommon<ChipInfo>(
context, context,
logger, logger,
windowManager, windowManager,
@@ -244,13 +290,16 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
tapGestureDetector, tapGestureDetector,
R.layout.media_ttt_chip R.layout.media_ttt_chip
) { ) {
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) { override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {
} }
} }
inner class TestChipState(appPackageName: String?) : MediaTttChipState(appPackageName) { inner class ChipInfo : ChipInfoCommon {
override fun getAppIcon(context: Context) = appIconDrawable override fun getTimeoutMs() = TIMEOUT_MS
} }
} }
private const val PACKAGE_NAME = "com.android.systemui" private const val PACKAGE_NAME = "com.android.systemui"
private const val APP_NAME = "Fake App Name"
private const val TIMEOUT_MS = 10000L

View File

@@ -157,44 +157,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
verify(logger).logStateChange(any(), any()) verify(logger).logStateChange(any(), any())
} }
@Test
fun displayChip_nullAppIconDrawable_iconIsFromPackageName() {
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, "appName")
controllerReceiver.displayChip(state)
assertThat(getChipView().getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
}
@Test
fun displayChip_hasAppIconDrawable_iconIsDrawable() {
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
val state = ChipStateReceiver(PACKAGE_NAME, drawable, "appName")
controllerReceiver.displayChip(state)
assertThat(getChipView().getAppIconView().drawable).isEqualTo(drawable)
}
@Test
fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() {
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName = null)
controllerReceiver.displayChip(state)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(APP_NAME)
}
@Test
fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() {
val appName = "Override App Name"
val state = ChipStateReceiver(PACKAGE_NAME, appIconDrawable = null, appName)
controllerReceiver.displayChip(state)
assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(appName)
}
private fun getChipView(): ViewGroup { private fun getChipView(): ViewGroup {
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())

View File

@@ -113,8 +113,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(almostCloseToStartCast().getChipTextString(context)) almostCloseToStartCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -125,8 +126,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(almostCloseToEndCast().getChipTextString(context)) almostCloseToEndCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -137,8 +139,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToReceiverTriggered().getChipTextString(context)) transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -149,8 +152,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToThisDeviceTriggered().getChipTextString(context)) transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -161,8 +165,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToReceiverSucceeded().getChipTextString(context)) transferToReceiverSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -173,8 +178,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToThisDeviceSucceeded().getChipTextString(context)) transferToThisDeviceSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -185,8 +191,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferFailed().getChipTextString(context)) transferToReceiverFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -197,8 +204,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null null
) )
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferFailed().getChipTextString(context)) transferToThisDeviceFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -250,7 +258,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -264,7 +274,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -278,7 +290,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -292,7 +306,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
@@ -306,7 +322,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
} }
@@ -355,8 +373,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
getChipView().getUndoButton().performClick() getChipView().getUndoButton().performClick()
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToThisDeviceTriggered().getChipTextString(context)) transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
@@ -367,7 +386,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(chipView.getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
} }
@@ -416,19 +437,38 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
getChipView().getUndoButton().performClick() getChipView().getUndoButton().performClick()
assertThat(getChipView().getChipText()) assertThat(getChipView().getChipText()).isEqualTo(
.isEqualTo(transferToReceiverTriggered().getChipTextString(context)) transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
)
} }
@Test @Test
fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() { fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferFailed() val state = transferToReceiverFailed()
controllerSender.displayChip(state) controllerSender.displayChip(state)
val chipView = getChipView() val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable) assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME) assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(chipView.getChipText()).isEqualTo(state.getChipTextString(context)) assertThat(getChipView().getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun transferToThisDeviceFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
val state = transferToThisDeviceFailed()
controllerSender.displayChip(state)
val chipView = getChipView()
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
assertThat(chipView.getAppIconView().contentDescription).isEqualTo(APP_NAME)
assertThat(getChipView().getChipText()).isEqualTo(
state.state.getChipTextString(context, OTHER_DEVICE_NAME)
)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE) assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE) assertThat(chipView.getUndoButton().visibility).isEqualTo(View.GONE)
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE) assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
@@ -475,7 +515,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Test @Test
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() { fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
controllerSender.displayChip(transferToReceiverTriggered()) controllerSender.displayChip(transferToReceiverTriggered())
controllerSender.displayChip(transferFailed()) controllerSender.displayChip(transferToReceiverFailed())
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE) assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
} }
@@ -498,7 +538,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L) fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason") controllerSender.removeChip("fakeRemovalReason")
fakeClock.advanceTime(state.getTimeoutMs() + 1) fakeClock.advanceTime(state.state.timeout + 1)
verify(windowManager).removeView(any()) verify(windowManager).removeView(any())
} }
@@ -521,7 +561,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeClock.advanceTime(1000L) fakeClock.advanceTime(1000L)
controllerSender.removeChip("fakeRemovalReason") controllerSender.removeChip("fakeRemovalReason")
fakeClock.advanceTime(state.getTimeoutMs() + 1) fakeClock.advanceTime(state.state.timeout + 1)
verify(windowManager).removeView(any()) verify(windowManager).removeView(any())
} }
@@ -546,34 +586,35 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToStartCast() = private fun almostCloseToStartCast() =
AlmostCloseToStartCast(PACKAGE_NAME, OTHER_DEVICE_NAME) ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_START_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun almostCloseToEndCast() = private fun almostCloseToEndCast() =
AlmostCloseToEndCast(PACKAGE_NAME, OTHER_DEVICE_NAME) ChipSenderInfo(ChipStateSender.ALMOST_CLOSE_TO_END_CAST, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverTriggered() = private fun transferToReceiverTriggered() =
TransferToReceiverTriggered(PACKAGE_NAME, OTHER_DEVICE_NAME) ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceTriggered() = private fun transferToThisDeviceTriggered() =
TransferToThisDeviceTriggered(PACKAGE_NAME) ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToReceiverSucceeded( ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED, routeInfo, undoCallback)
PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) = private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToThisDeviceSucceeded( ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED, routeInfo, undoCallback)
PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
)
/** Helper method providing default parameters to not clutter up the tests. */ /** Helper method providing default parameters to not clutter up the tests. */
private fun transferFailed() = TransferFailed(PACKAGE_NAME) private fun transferToReceiverFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToThisDeviceFailed() =
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED, routeInfo)
} }
private const val APP_NAME = "Fake app name" private const val APP_NAME = "Fake app name"