Merge "[Media TTT] Refactor media states to use an enum instead of sealed classes." into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
bb1a04a502
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.media.taptotransfer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.media.MediaRoute2Info
|
||||
@@ -23,16 +24,12 @@ import android.util.Log
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
|
||||
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
|
||||
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.media.taptotransfer.receiver.ChipStateReceiver
|
||||
import com.android.systemui.media.taptotransfer.sender.ChipStateSender
|
||||
import com.android.systemui.statusbar.commandline.Command
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import java.io.PrintWriter
|
||||
import java.lang.IllegalArgumentException
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -46,28 +43,6 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
private val context: Context,
|
||||
@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 {
|
||||
commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
|
||||
commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
|
||||
@@ -76,21 +51,23 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
/** All commands for the sender device. */
|
||||
inner class SenderCommand : Command {
|
||||
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]
|
||||
@StatusBarManager.MediaTransferSenderState
|
||||
val displayState = stateStringToStateInt[commandName]
|
||||
if (displayState == null) {
|
||||
val displayState: Int?
|
||||
try {
|
||||
displayState = ChipStateSender.getSenderStateIdFromName(commandName)
|
||||
} catch (ex: IllegalArgumentException) {
|
||||
pw.println("Invalid command name $commandName")
|
||||
return
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant") // sysui allowed to call STATUS_BAR_SERVICE
|
||||
val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
|
||||
as StatusBarManager
|
||||
val routeInfo = MediaRoute2Info.Builder("id", args[0])
|
||||
.addFeature("feature")
|
||||
.setPackageName(TEST_PACKAGE_NAME)
|
||||
.build()
|
||||
statusBarManager.updateMediaTapToTransferSenderDisplay(
|
||||
displayState,
|
||||
routeInfo,
|
||||
@@ -136,6 +113,17 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
/** All commands for the receiver device. */
|
||||
inner class ReceiverCommand : Command {
|
||||
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)
|
||||
as StatusBarManager
|
||||
val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
|
||||
@@ -143,24 +131,12 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
.setPackageName(TEST_PACKAGE_NAME)
|
||||
.build()
|
||||
|
||||
when(val commandName = args[0]) {
|
||||
CLOSE_TO_SENDER_STATE ->
|
||||
statusBarManager.updateMediaTapToTransferReceiverDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
|
||||
routeInfo,
|
||||
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")
|
||||
}
|
||||
statusBarManager.updateMediaTapToTransferReceiverDisplay(
|
||||
displayState,
|
||||
routeInfo,
|
||||
null,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
override fun help(pw: PrintWriter) {
|
||||
@@ -173,11 +149,5 @@ class MediaTttCommandLineHelper @Inject constructor(
|
||||
const val SENDER_COMMAND = "media-ttt-chip-sender"
|
||||
@VisibleForTesting
|
||||
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 TEST_PACKAGE_NAME = "com.android.systemui"
|
||||
|
||||
@@ -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
|
||||
@@ -19,14 +19,16 @@ package com.android.systemui.media.taptotransfer.common
|
||||
import android.annotation.LayoutRes
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.Log
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.systemui.R
|
||||
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
|
||||
* 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 logger: MediaTttLogger,
|
||||
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. */
|
||||
var chipView: ViewGroup? = null
|
||||
private var chipView: ViewGroup? = null
|
||||
|
||||
/** 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.
|
||||
@@ -75,7 +80,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
* This method handles inflating and attaching the view, then delegates to [updateChipView] to
|
||||
* display the correct information in the chip.
|
||||
*/
|
||||
fun displayChip(chipState: T) {
|
||||
fun displayChip(chipInfo: T) {
|
||||
val oldChipView = chipView
|
||||
if (chipView == null) {
|
||||
chipView = LayoutInflater
|
||||
@@ -84,7 +89,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
}
|
||||
val currentChipView = chipView!!
|
||||
|
||||
updateChipView(chipState, currentChipView)
|
||||
updateChipView(chipInfo, currentChipView)
|
||||
|
||||
// Add view if necessary
|
||||
if (oldChipView == null) {
|
||||
@@ -96,7 +101,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
cancelChipViewTimeout?.run()
|
||||
cancelChipViewTimeout = mainExecutor.executeDelayed(
|
||||
{ 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.
|
||||
*
|
||||
* 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)
|
||||
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) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
@@ -140,6 +153,30 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
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) {
|
||||
val view = chipView ?: return
|
||||
// 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_SCREEN_TAP = "SCREEN_TAP"
|
||||
}
|
||||
|
||||
|
||||
@@ -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!!
|
||||
@@ -16,35 +16,41 @@
|
||||
|
||||
package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttChipState
|
||||
import android.app.StatusBarManager
|
||||
import com.android.systemui.media.taptotransfer.common.DEFAULT_TIMEOUT_MILLIS
|
||||
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
|
||||
* 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(
|
||||
appPackageName: String?,
|
||||
private val appIconDrawable: Drawable?,
|
||||
private val appName: CharSequence?
|
||||
) : MediaTttChipState(appPackageName) {
|
||||
override fun getAppIcon(context: Context): Drawable? {
|
||||
if (appIconDrawable != null) {
|
||||
return appIconDrawable
|
||||
}
|
||||
return super.getAppIcon(context)
|
||||
}
|
||||
enum class ChipStateReceiver(
|
||||
@StatusBarManager.MediaTransferSenderState val stateInt: Int,
|
||||
) {
|
||||
CLOSE_TO_SENDER(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
|
||||
),
|
||||
FAR_FROM_SENDER(
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
|
||||
);
|
||||
|
||||
override fun getAppName(context: Context): String? {
|
||||
if (appName != null) {
|
||||
return appName.toString()
|
||||
}
|
||||
return super.getAppName(context)
|
||||
companion object {
|
||||
/**
|
||||
* Returns the receiver state enum associated with the given [displayState] from
|
||||
* [StatusBarManager].
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.media.taptotransfer.receiver
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.MediaRoute2Info
|
||||
import android.os.Handler
|
||||
@@ -27,6 +28,8 @@ import android.view.WindowManager
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
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.MediaTttLogger
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
@@ -50,7 +53,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
mainExecutor: DelayableExecutor,
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
@Main private val mainHandler: Handler,
|
||||
) : MediaTttChipControllerCommon<ChipStateReceiver>(
|
||||
) : MediaTttChipControllerCommon<ChipReceiverInfo>(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
@@ -82,45 +85,50 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
appIcon: Icon?,
|
||||
appName: CharSequence?
|
||||
) {
|
||||
logger.logStateChange(stateIntToString(displayState), routeInfo.id)
|
||||
when(displayState) {
|
||||
StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER -> {
|
||||
val packageName = routeInfo.packageName
|
||||
if (appIcon == null) {
|
||||
displayChip(ChipStateReceiver(packageName, null, appName))
|
||||
} else {
|
||||
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")
|
||||
val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
|
||||
val stateName = chipState?.name ?: "Invalid"
|
||||
logger.logStateChange(stateName, routeInfo.id)
|
||||
|
||||
if (chipState == null) {
|
||||
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
|
||||
return
|
||||
}
|
||||
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) {
|
||||
setIcon(chipState, currentChipView)
|
||||
}
|
||||
|
||||
private fun stateIntToString(@StatusBarManager.MediaTransferReceiverState state: Int): String {
|
||||
return when (state) {
|
||||
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"
|
||||
}
|
||||
override fun updateChipView(chipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
|
||||
setIcon(
|
||||
currentChipView,
|
||||
chipInfo.routeInfo.packageName,
|
||||
chipInfo.appIconDrawableOverride,
|
||||
chipInfo.appNameOverride
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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 CLOSE_TO_SENDER = "CLOSE_TO_SENDER"
|
||||
private const val FAR_FROM_SENDER = "FAR_FROM_SENDER"
|
||||
|
||||
@@ -16,27 +16,168 @@
|
||||
|
||||
package com.android.systemui.media.taptotransfer.sender
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.media.MediaRoute2Info
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
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
|
||||
* the sender device.
|
||||
* A class enumerating all the possible states of the media tap-to-transfer chip on the sender
|
||||
* device.
|
||||
*
|
||||
* This is a sealed class where each subclass represents a specific chip state. Each subclass can
|
||||
* contain additional information that is necessary for only that state.
|
||||
* @property stateInt the integer from [StatusBarManager] corresponding with this 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(
|
||||
appPackageName: String?
|
||||
) : MediaTttChipState(appPackageName) {
|
||||
/** Returns a fully-formed string with the text that the chip should display. */
|
||||
abstract fun getChipTextString(context: Context): String
|
||||
enum class ChipStateSender(
|
||||
@StatusBarManager.MediaTransferSenderState val stateInt: Int,
|
||||
@StringRes val stringResId: Int?,
|
||||
val isMidTransfer: Boolean = false,
|
||||
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
|
||||
@@ -44,160 +185,36 @@ sealed class ChipStateSender(
|
||||
*
|
||||
* @param controllerSender passed as a parameter in case we want to display a new chip state
|
||||
* 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(
|
||||
controllerSender: MediaTttChipControllerSender
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?
|
||||
): View.OnClickListener? = null
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @property otherDeviceName the name of the other device involved in the transfer.
|
||||
*/
|
||||
class AlmostCloseToStartCast(
|
||||
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)
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
/**
|
||||
* Returns the sender state enum associated with the given [displayState] from
|
||||
* [StatusBarManager].
|
||||
*/
|
||||
fun getSenderStateFromId(
|
||||
@StatusBarManager.MediaTransferSenderState displayState: Int,
|
||||
): ChipStateSender = values().first { it.stateInt == displayState }
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* @property otherDeviceName the name of the other device involved in the transfer.
|
||||
*/
|
||||
class AlmostCloseToEndCast(
|
||||
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)
|
||||
/**
|
||||
* Returns the state int from [StatusBarManager] associated with the given sender state
|
||||
* name.
|
||||
*
|
||||
* @param name the name of one of the [ChipStateSender] enums.
|
||||
*/
|
||||
@StatusBarManager.MediaTransferSenderState
|
||||
fun getSenderStateIdFromName(name: String): Int = valueOf(name).stateInt
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// continue indefinitely).
|
||||
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
|
||||
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
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.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
|
||||
@@ -50,7 +51,7 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
viewUtil: ViewUtil,
|
||||
@Main mainExecutor: DelayableExecutor,
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
) : MediaTttChipControllerCommon<ChipStateSender>(
|
||||
) : MediaTttChipControllerCommon<ChipSenderInfo>(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
@@ -82,104 +83,82 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
routeInfo: MediaRoute2Info,
|
||||
undoCallback: IUndoMediaTransferCallback?
|
||||
) {
|
||||
logger.logStateChange(stateIntToString(displayState), routeInfo.id)
|
||||
val appPackageName = routeInfo.packageName
|
||||
val otherDeviceName = routeInfo.name.toString()
|
||||
val chipState = when(displayState) {
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
|
||||
AlmostCloseToStartCast(appPackageName, otherDeviceName)
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
|
||||
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
|
||||
}
|
||||
val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
|
||||
val stateName = chipState?.name ?: "Invalid"
|
||||
logger.logStateChange(stateName, routeInfo.id)
|
||||
|
||||
if (chipState == null) {
|
||||
Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
|
||||
return
|
||||
}
|
||||
|
||||
chipState?.let {
|
||||
displayChip(it)
|
||||
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
|
||||
removeChip(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
|
||||
} else {
|
||||
displayChip(ChipSenderInfo(chipState, routeInfo, undoCallback))
|
||||
}
|
||||
}
|
||||
|
||||
/** 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
|
||||
|
||||
// App icon
|
||||
setIcon(chipState, currentChipView)
|
||||
setIcon(currentChipView, chipInfo.routeInfo.packageName)
|
||||
|
||||
// Text
|
||||
val otherDeviceName = chipInfo.routeInfo.name.toString()
|
||||
currentChipView.requireViewById<TextView>(R.id.text).apply {
|
||||
text = chipState.getChipTextString(context)
|
||||
text = chipState.getChipTextString(context, otherDeviceName)
|
||||
}
|
||||
|
||||
// Loading
|
||||
currentChipView.requireViewById<View>(R.id.loading).visibility =
|
||||
if (chipState.showLoading()) { View.VISIBLE } else { View.GONE }
|
||||
chipState.isMidTransfer.visibleIfTrue()
|
||||
|
||||
|
||||
// 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.visibility = if (undoClickListener != null) { View.VISIBLE } else { View.GONE }
|
||||
undoView.visibility = (undoClickListener != null).visibleIfTrue()
|
||||
|
||||
// Failure
|
||||
val showFailure = chipState is TransferFailed
|
||||
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
|
||||
if (showFailure) { View.VISIBLE } else { View.GONE }
|
||||
chipState.isTransferFailure.visibleIfTrue()
|
||||
}
|
||||
|
||||
override fun removeChip(removalReason: String) {
|
||||
// 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.)
|
||||
if ((currentlyDisplayedChipState is TransferToReceiverTriggered ||
|
||||
currentlyDisplayedChipState is TransferToThisDeviceTriggered)
|
||||
&& removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
|
||||
if (currentlyDisplayedChipState?.isMidTransfer == true
|
||||
&& removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
|
||||
return
|
||||
}
|
||||
super.removeChip(removalReason)
|
||||
currentlyDisplayedChipState = null
|
||||
}
|
||||
|
||||
private fun stateIntToString(@StatusBarManager.MediaTransferSenderState state: Int): String {
|
||||
return when(state) {
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
|
||||
"ALMOST_CLOSE_TO_START_CAST"
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
|
||||
"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"
|
||||
private fun Boolean.visibleIfTrue(): Int {
|
||||
return if (this) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ChipSenderInfo(
|
||||
val state: ChipStateSender,
|
||||
val routeInfo: MediaRoute2Info,
|
||||
val undoCallback: IUndoMediaTransferCallback? = null
|
||||
) : ChipInfoCommon {
|
||||
override fun getTimeoutMs() = state.timeout
|
||||
}
|
||||
|
||||
const val SENDER_TAG = "MediaTapToTransferSender"
|
||||
private const val FAR_FROM_RECEIVER = "FAR_FROM_RECEIVER"
|
||||
|
||||
@@ -21,13 +21,8 @@ import android.content.Context
|
||||
import android.media.MediaRoute2Info
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
|
||||
import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
|
||||
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.media.taptotransfer.receiver.ChipStateReceiver
|
||||
import com.android.systemui.media.taptotransfer.sender.ChipStateSender
|
||||
import com.android.systemui.statusbar.commandline.Command
|
||||
import com.android.systemui.statusbar.commandline.CommandRegistry
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
@@ -88,7 +83,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_almostCloseToStartCast_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(AlmostCloseToStartCast::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_START_CAST.name)
|
||||
)
|
||||
|
||||
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
|
||||
@@ -103,7 +98,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_almostCloseToEndCast_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(AlmostCloseToEndCast::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.ALMOST_CLOSE_TO_END_CAST.name)
|
||||
)
|
||||
|
||||
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
|
||||
@@ -118,7 +113,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(TransferToReceiverTriggered::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_TRIGGERED.name)
|
||||
)
|
||||
|
||||
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
|
||||
@@ -133,7 +128,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(TransferToThisDeviceTriggered::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_TRIGGERED.name)
|
||||
)
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
|
||||
@@ -146,7 +141,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(TransferToReceiverSucceeded::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED.name)
|
||||
)
|
||||
|
||||
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
|
||||
@@ -161,7 +156,7 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(TransferToThisDeviceSucceeded::class.simpleName!!)
|
||||
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED.name)
|
||||
)
|
||||
|
||||
val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
|
||||
@@ -174,8 +169,10 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sender_transferFailed_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(pw, getSenderCommand(TransferFailed::class.simpleName!!))
|
||||
fun sender_transferToReceiverFailed_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(ChipStateSender.TRANSFER_TO_RECEIVER_FAILED.name)
|
||||
)
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED),
|
||||
@@ -184,9 +181,24 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
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
|
||||
fun sender_farFromReceiver_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(pw, getSenderCommand(FAR_FROM_RECEIVER_STATE))
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getSenderCommand(ChipStateSender.FAR_FROM_RECEIVER.name)
|
||||
)
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER),
|
||||
@@ -197,7 +209,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun receiver_closeToSender_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE))
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getReceiverCommand(ChipStateReceiver.CLOSE_TO_SENDER.name)
|
||||
)
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
|
||||
@@ -209,7 +223,9 @@ class MediaTttCommandLineHelperTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun receiver_farFromSender_serviceCallbackCalled() {
|
||||
commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE))
|
||||
commandRegistry.onShellCommand(
|
||||
pw, getReceiverCommand(ChipStateReceiver.FAR_FROM_SENDER.name)
|
||||
)
|
||||
|
||||
verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
|
||||
eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
package com.android.systemui.media.taptotransfer.common
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.MediaRoute2Info
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@@ -27,6 +30,7 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
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.util.concurrency.DelayableExecutor
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
@@ -40,6 +44,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.eq
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.reset
|
||||
import org.mockito.Mockito.verify
|
||||
@@ -48,12 +53,16 @@ import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
|
||||
private lateinit var controllerCommon: MediaTttChipControllerCommon<ChipInfo>
|
||||
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
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
|
||||
private lateinit var logger: MediaTttLogger
|
||||
@Mock
|
||||
@@ -66,7 +75,15 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
@Before
|
||||
fun setUp() {
|
||||
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()
|
||||
fakeExecutor = FakeExecutor(fakeClock)
|
||||
|
||||
@@ -100,7 +117,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.displayChip(state)
|
||||
reset(windowManager)
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() - 1)
|
||||
fakeClock.advanceTime(TIMEOUT_MS - 1)
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
@@ -111,7 +128,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.displayChip(state)
|
||||
reset(windowManager)
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
@@ -128,7 +145,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.displayChip(getState())
|
||||
|
||||
// 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(windowManager, never()).removeView(any())
|
||||
@@ -145,7 +162,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.displayChip(getState())
|
||||
|
||||
// Ensure we still hide the chip eventually
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
fakeClock.advanceTime(TIMEOUT_MS + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
@@ -172,16 +189,45 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_viewHasIconAndContentDescription() {
|
||||
fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
|
||||
controllerCommon.displayChip(getState())
|
||||
val chipView = getChipView()
|
||||
|
||||
val state = TestChipState(PACKAGE_NAME)
|
||||
controllerCommon.setIcon(state, chipView)
|
||||
controllerCommon.setIcon(chipView, PACKAGE_NAME, appIconDrawableOverride = null, null)
|
||||
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconDrawable)
|
||||
assertThat(chipView.getAppIconView().contentDescription)
|
||||
.isEqualTo(state.getAppName(context))
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -218,7 +264,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
private fun getState() = MediaTttChipState(PACKAGE_NAME)
|
||||
private fun getState() = ChipInfo()
|
||||
|
||||
private fun getChipView(): ViewGroup {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
@@ -235,7 +281,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
viewUtil: ViewUtil,
|
||||
@Main mainExecutor: DelayableExecutor,
|
||||
tapGestureDetector: TapGestureDetector,
|
||||
) : MediaTttChipControllerCommon<MediaTttChipState>(
|
||||
) : MediaTttChipControllerCommon<ChipInfo>(
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
@@ -244,13 +290,16 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
tapGestureDetector,
|
||||
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) {
|
||||
override fun getAppIcon(context: Context) = appIconDrawable
|
||||
inner class ChipInfo : ChipInfoCommon {
|
||||
override fun getTimeoutMs() = TIMEOUT_MS
|
||||
}
|
||||
}
|
||||
|
||||
private const val PACKAGE_NAME = "com.android.systemui"
|
||||
private const val APP_NAME = "Fake App Name"
|
||||
private const val TIMEOUT_MS = 10000L
|
||||
|
||||
@@ -157,44 +157,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
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 {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
|
||||
@@ -113,8 +113,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(almostCloseToStartCast().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
almostCloseToStartCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -125,8 +126,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(almostCloseToEndCast().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
almostCloseToEndCast().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,8 +139,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToReceiverTriggered().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -149,8 +152,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -161,8 +165,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToReceiverSucceeded().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToReceiverSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -173,8 +178,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToThisDeviceSucceeded().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToThisDeviceSucceeded().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -185,8 +191,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferFailed().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToReceiverFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -197,8 +204,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferFailed().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToThisDeviceFailed().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -250,7 +258,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getUndoButton().visibility).isEqualTo(View.GONE)
|
||||
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
@@ -264,7 +274,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getUndoButton().visibility).isEqualTo(View.GONE)
|
||||
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
@@ -278,7 +290,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getUndoButton().visibility).isEqualTo(View.GONE)
|
||||
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
@@ -292,7 +306,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getUndoButton().visibility).isEqualTo(View.GONE)
|
||||
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
@@ -306,7 +322,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
@@ -355,8 +373,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
|
||||
getChipView().getUndoButton().performClick()
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToThisDeviceTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -367,7 +386,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getFailureIcon().visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
@@ -416,19 +437,38 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
|
||||
getChipView().getUndoButton().performClick()
|
||||
|
||||
assertThat(getChipView().getChipText())
|
||||
.isEqualTo(transferToReceiverTriggered().getChipTextString(context))
|
||||
assertThat(getChipView().getChipText()).isEqualTo(
|
||||
transferToReceiverTriggered().state.getChipTextString(context, OTHER_DEVICE_NAME)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
|
||||
val state = transferFailed()
|
||||
fun transferToReceiverFailed_appIcon_noDeviceName_noLoadingIcon_noUndo_failureIcon() {
|
||||
val state = transferToReceiverFailed()
|
||||
controllerSender.displayChip(state)
|
||||
|
||||
val chipView = getChipView()
|
||||
assertThat(chipView.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
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.getUndoButton().visibility).isEqualTo(View.GONE)
|
||||
assertThat(chipView.getFailureIcon().visibility).isEqualTo(View.VISIBLE)
|
||||
@@ -475,7 +515,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun changeFromTransferTriggeredToTransferFailed_failureIconAppears() {
|
||||
controllerSender.displayChip(transferToReceiverTriggered())
|
||||
controllerSender.displayChip(transferFailed())
|
||||
controllerSender.displayChip(transferToReceiverFailed())
|
||||
|
||||
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
@@ -498,7 +538,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
fakeClock.advanceTime(state.state.timeout + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
@@ -521,7 +561,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
fakeClock.advanceTime(state.state.timeout + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
@@ -546,34 +586,35 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
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. */
|
||||
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. */
|
||||
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. */
|
||||
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. */
|
||||
private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
|
||||
TransferToReceiverSucceeded(
|
||||
PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
|
||||
)
|
||||
ChipSenderInfo(ChipStateSender.TRANSFER_TO_RECEIVER_SUCCEEDED, routeInfo, undoCallback)
|
||||
|
||||
/** Helper method providing default parameters to not clutter up the tests. */
|
||||
private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
|
||||
TransferToThisDeviceSucceeded(
|
||||
PACKAGE_NAME, OTHER_DEVICE_NAME, undoCallback
|
||||
)
|
||||
ChipSenderInfo(ChipStateSender.TRANSFER_TO_THIS_DEVICE_SUCCEEDED, routeInfo, undoCallback)
|
||||
|
||||
/** 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"
|
||||
|
||||
Reference in New Issue
Block a user