Merge changes I5b86508e,I01cd3be5,I5f21d737,Ibae88dbf into tm-qpr-dev
* changes: [Media TTT] Put package error logs in the log buffer with all the other logs. [Media TTT] Also log the package name on each state change. [Media TTT] Ignore the FAR_FROM_RECEIVER event if we're in the succeeded state. [Chipbar] Remove #setIcon from TemporaryViewDisplayController and put it in MediaTttUtils instead.
This commit is contained in:
committed by
Android (Google) Code Review
commit
1e3451782b
@@ -28,28 +28,41 @@ class MediaTttLogger(
|
||||
private val deviceTypeTag: String,
|
||||
private val buffer: LogBuffer
|
||||
){
|
||||
private val bufferTag = BASE_TAG + deviceTypeTag
|
||||
|
||||
/** Logs a change in the chip state for the given [mediaRouteId]. */
|
||||
fun logStateChange(stateName: String, mediaRouteId: String) {
|
||||
fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
|
||||
buffer.log(
|
||||
BASE_TAG + deviceTypeTag,
|
||||
bufferTag,
|
||||
LogLevel.DEBUG,
|
||||
{
|
||||
str1 = stateName
|
||||
str2 = mediaRouteId
|
||||
str3 = packageName
|
||||
},
|
||||
{ "State changed to $str1 for ID=$str2" }
|
||||
{ "State changed to $str1 for ID=$str2 package=$str3" }
|
||||
)
|
||||
}
|
||||
|
||||
/** Logs that we removed the chip for the given [reason]. */
|
||||
fun logChipRemoval(reason: String) {
|
||||
buffer.log(
|
||||
BASE_TAG + deviceTypeTag,
|
||||
bufferTag,
|
||||
LogLevel.DEBUG,
|
||||
{ str1 = reason },
|
||||
{ "Chip removed due to $str1" }
|
||||
)
|
||||
}
|
||||
|
||||
/** Logs that we couldn't find information for [packageName]. */
|
||||
fun logPackageNotFound(packageName: String) {
|
||||
buffer.log(
|
||||
bufferTag,
|
||||
LogLevel.DEBUG,
|
||||
{ str1 = packageName },
|
||||
{ "Package $str1 could not be found" }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val BASE_TAG = "MediaTtt"
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.taptotransfer.common
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.R
|
||||
|
||||
/** Utility methods for media tap-to-transfer. */
|
||||
class MediaTttUtils {
|
||||
companion object {
|
||||
/**
|
||||
* Returns the information needed to display the icon.
|
||||
*
|
||||
* The information will either contain app name and icon of the app playing media, or a
|
||||
* default name and icon if we can't find the app name/icon.
|
||||
*
|
||||
* @param appPackageName the package name of the app playing the media.
|
||||
* @param logger the logger to use for any errors.
|
||||
*/
|
||||
fun getIconInfoFromPackageName(
|
||||
context: Context,
|
||||
appPackageName: String?,
|
||||
logger: MediaTttLogger
|
||||
): IconInfo {
|
||||
if (appPackageName != null) {
|
||||
try {
|
||||
val contentDescription =
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
appPackageName,
|
||||
PackageManager.ApplicationInfoFlags.of(0)
|
||||
)
|
||||
.loadLabel(context.packageManager)
|
||||
.toString()
|
||||
return IconInfo(
|
||||
contentDescription,
|
||||
drawable = context.packageManager.getApplicationIcon(appPackageName),
|
||||
isAppIcon = true
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
logger.logPackageNotFound(appPackageName)
|
||||
}
|
||||
}
|
||||
return IconInfo(
|
||||
contentDescription =
|
||||
context.getString(R.string.media_output_dialog_unknown_launch_app_name),
|
||||
drawable =
|
||||
context.resources.getDrawable(R.drawable.ic_cast).apply {
|
||||
this.setTint(
|
||||
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
|
||||
)
|
||||
},
|
||||
isAppIcon = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an icon to be displayed by the given view.
|
||||
*
|
||||
* @param iconSize the size in pixels that the icon should be. If null, the size of
|
||||
* [appIconView] will not be adjusted.
|
||||
*/
|
||||
fun setIcon(
|
||||
appIconView: CachingIconView,
|
||||
icon: Drawable,
|
||||
iconContentDescription: CharSequence,
|
||||
iconSize: Int? = null,
|
||||
) {
|
||||
iconSize?.let { size ->
|
||||
val lp = appIconView.layoutParams
|
||||
lp.width = size
|
||||
lp.height = size
|
||||
appIconView.layoutParams = lp
|
||||
}
|
||||
|
||||
appIconView.contentDescription = iconContentDescription
|
||||
appIconView.setImageDrawable(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class IconInfo(
|
||||
val contentDescription: String,
|
||||
val drawable: Drawable,
|
||||
/**
|
||||
* True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
|
||||
*/
|
||||
val isAppIcon: Boolean
|
||||
)
|
||||
@@ -35,6 +35,7 @@ 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.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||
@@ -107,7 +108,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
) {
|
||||
val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
|
||||
val stateName = chipState?.name ?: "Invalid"
|
||||
logger.logStateChange(stateName, routeInfo.id)
|
||||
logger.logStateChange(stateName, routeInfo.id, routeInfo.clientPackageName)
|
||||
|
||||
if (chipState == null) {
|
||||
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
|
||||
@@ -137,13 +138,26 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
|
||||
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
|
||||
super.updateView(newInfo, currentView)
|
||||
val iconName = setIcon(
|
||||
currentView,
|
||||
newInfo.routeInfo.clientPackageName,
|
||||
newInfo.appIconDrawableOverride,
|
||||
newInfo.appNameOverride
|
||||
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
)
|
||||
val iconDrawable = newInfo.appIconDrawableOverride ?: iconInfo.drawable
|
||||
val iconContentDescription = newInfo.appNameOverride ?: iconInfo.contentDescription
|
||||
val iconSize = context.resources.getDimensionPixelSize(
|
||||
if (iconInfo.isAppIcon) {
|
||||
R.dimen.media_ttt_icon_size_receiver
|
||||
} else {
|
||||
R.dimen.media_ttt_generic_icon_size_receiver
|
||||
}
|
||||
)
|
||||
|
||||
MediaTttUtils.setIcon(
|
||||
currentView.requireViewById(R.id.app_icon),
|
||||
iconDrawable,
|
||||
iconContentDescription,
|
||||
iconSize,
|
||||
)
|
||||
currentView.contentDescription = iconName
|
||||
}
|
||||
|
||||
override fun animateViewIn(view: ViewGroup) {
|
||||
@@ -161,15 +175,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
|
||||
startRipple(view.requireViewById(R.id.ripple))
|
||||
}
|
||||
|
||||
override fun getIconSize(isAppIcon: Boolean): Int? =
|
||||
context.resources.getDimensionPixelSize(
|
||||
if (isAppIcon) {
|
||||
R.dimen.media_ttt_icon_size_receiver
|
||||
} else {
|
||||
R.dimen.media_ttt_generic_icon_size_receiver
|
||||
}
|
||||
)
|
||||
|
||||
/** Returns the amount that the chip will be translated by in its intro animation. */
|
||||
private fun getTranslationAmount(): Int {
|
||||
return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
|
||||
|
||||
@@ -34,8 +34,7 @@ import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
|
||||
* @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 transferStatus the transfer status that the chip state represents.
|
||||
* @property timeout the amount of time this chip should display on the screen before it times out
|
||||
* and disappears.
|
||||
*/
|
||||
@@ -43,8 +42,7 @@ enum class ChipStateSender(
|
||||
@StatusBarManager.MediaTransferSenderState val stateInt: Int,
|
||||
val uiEvent: UiEventLogger.UiEventEnum,
|
||||
@StringRes val stringResId: Int?,
|
||||
val isMidTransfer: Boolean = false,
|
||||
val isTransferFailure: Boolean = false,
|
||||
val transferStatus: TransferStatus,
|
||||
val timeout: Long = DEFAULT_TIMEOUT_MILLIS
|
||||
) {
|
||||
/**
|
||||
@@ -56,6 +54,7 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST,
|
||||
R.string.media_move_closer_to_start_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
),
|
||||
|
||||
/**
|
||||
@@ -68,6 +67,7 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST,
|
||||
R.string.media_move_closer_to_end_cast,
|
||||
transferStatus = TransferStatus.NOT_STARTED,
|
||||
),
|
||||
|
||||
/**
|
||||
@@ -78,7 +78,7 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED,
|
||||
R.string.media_transfer_playing_different_device,
|
||||
isMidTransfer = true,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
),
|
||||
|
||||
@@ -90,7 +90,7 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
|
||||
R.string.media_transfer_playing_this_device,
|
||||
isMidTransfer = true,
|
||||
transferStatus = TransferStatus.IN_PROGRESS,
|
||||
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
),
|
||||
|
||||
@@ -100,7 +100,8 @@ enum class ChipStateSender(
|
||||
TRANSFER_TO_RECEIVER_SUCCEEDED(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED,
|
||||
R.string.media_transfer_playing_different_device
|
||||
R.string.media_transfer_playing_different_device,
|
||||
transferStatus = TransferStatus.SUCCEEDED,
|
||||
) {
|
||||
override fun undoClickListener(
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
@@ -135,7 +136,8 @@ enum class ChipStateSender(
|
||||
TRANSFER_TO_THIS_DEVICE_SUCCEEDED(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
|
||||
R.string.media_transfer_playing_this_device
|
||||
R.string.media_transfer_playing_this_device,
|
||||
transferStatus = TransferStatus.SUCCEEDED,
|
||||
) {
|
||||
override fun undoClickListener(
|
||||
controllerSender: MediaTttChipControllerSender,
|
||||
@@ -169,7 +171,7 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED,
|
||||
R.string.media_transfer_failed,
|
||||
isTransferFailure = true
|
||||
transferStatus = TransferStatus.FAILED,
|
||||
),
|
||||
|
||||
/** A state representing that a transfer back to this device has failed. */
|
||||
@@ -177,14 +179,15 @@ enum class ChipStateSender(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED,
|
||||
R.string.media_transfer_failed,
|
||||
isTransferFailure = true
|
||||
transferStatus = TransferStatus.FAILED,
|
||||
),
|
||||
|
||||
/** A state representing that this device is far away from any receiver device. */
|
||||
FAR_FROM_RECEIVER(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER,
|
||||
stringResId = null
|
||||
stringResId = null,
|
||||
transferStatus = TransferStatus.TOO_FAR,
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.android.systemui.animation.ViewHierarchyAnimator
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
|
||||
@@ -94,7 +95,7 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
) {
|
||||
val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
|
||||
val stateName = chipState?.name ?: "Invalid"
|
||||
logger.logStateChange(stateName, routeInfo.id)
|
||||
logger.logStateChange(stateName, routeInfo.id, routeInfo.clientPackageName)
|
||||
|
||||
if (chipState == null) {
|
||||
Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
|
||||
@@ -118,7 +119,14 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
val chipState = newInfo.state
|
||||
|
||||
// App icon
|
||||
val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName)
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
)
|
||||
MediaTttUtils.setIcon(
|
||||
currentView.requireViewById(R.id.app_icon),
|
||||
iconInfo.drawable,
|
||||
iconInfo.contentDescription
|
||||
)
|
||||
|
||||
// Text
|
||||
val otherDeviceName = newInfo.routeInfo.name.toString()
|
||||
@@ -127,7 +135,7 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
|
||||
// Loading
|
||||
currentView.requireViewById<View>(R.id.loading).visibility =
|
||||
chipState.isMidTransfer.visibleIfTrue()
|
||||
(chipState.transferStatus == TransferStatus.IN_PROGRESS).visibleIfTrue()
|
||||
|
||||
// Undo
|
||||
val undoView = currentView.requireViewById<View>(R.id.undo)
|
||||
@@ -139,12 +147,12 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
|
||||
// Failure
|
||||
currentView.requireViewById<View>(R.id.failure_icon).visibility =
|
||||
chipState.isTransferFailure.visibleIfTrue()
|
||||
(chipState.transferStatus == TransferStatus.FAILED).visibleIfTrue()
|
||||
|
||||
// For accessibility
|
||||
currentView.requireViewById<ViewGroup>(
|
||||
R.id.media_ttt_sender_chip_inner
|
||||
).contentDescription = "$iconName $chipText"
|
||||
).contentDescription = "${iconInfo.contentDescription} $chipText"
|
||||
}
|
||||
|
||||
override fun animateViewIn(view: ViewGroup) {
|
||||
@@ -162,10 +170,14 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
}
|
||||
|
||||
override fun removeView(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 (info?.state?.isMidTransfer == true &&
|
||||
removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) {
|
||||
// Don't remove the chip if we're in progress or succeeded, since the user should still be
|
||||
// able to see the status of the transfer. (But do remove it if it's finally timed out.)
|
||||
val transferStatus = info?.state?.transferStatus
|
||||
if (
|
||||
(transferStatus == TransferStatus.IN_PROGRESS ||
|
||||
transferStatus == TransferStatus.SUCCEEDED) &&
|
||||
removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT
|
||||
) {
|
||||
return
|
||||
}
|
||||
super.removeView(removalReason)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.taptotransfer.sender
|
||||
|
||||
/** Represents the different possible transfer states that we could be in. */
|
||||
enum class TransferStatus {
|
||||
/** The transfer hasn't started yet. */
|
||||
NOT_STARTED,
|
||||
/** The transfer is currently ongoing but hasn't completed yet. */
|
||||
IN_PROGRESS,
|
||||
/** The transfer has completed successfully. */
|
||||
SUCCEEDED,
|
||||
/** The transfer has completed with a failure. */
|
||||
FAILED,
|
||||
/** The device is too far away to do a transfer. */
|
||||
TOO_FAR,
|
||||
}
|
||||
@@ -19,12 +19,10 @@ package com.android.systemui.temporarydisplay
|
||||
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.os.PowerManager
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
@@ -33,9 +31,6 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
|
||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
|
||||
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
|
||||
import androidx.annotation.CallSuper
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
@@ -192,79 +187,11 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
|
||||
* appears.
|
||||
*/
|
||||
open fun animateViewIn(view: ViewGroup) {}
|
||||
|
||||
/**
|
||||
* Returns the size that the icon should be, or null if no size override is needed.
|
||||
*/
|
||||
open fun getIconSize(isAppIcon: Boolean): Int? = null
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return the content description of the icon.
|
||||
*/
|
||||
internal fun setIcon(
|
||||
currentView: ViewGroup,
|
||||
appPackageName: String?,
|
||||
appIconDrawableOverride: Drawable? = null,
|
||||
appNameOverride: CharSequence? = null,
|
||||
): CharSequence {
|
||||
val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
|
||||
val iconInfo = getIconInfo(appPackageName)
|
||||
|
||||
getIconSize(iconInfo.isAppIcon)?.let { size ->
|
||||
val lp = appIconView.layoutParams
|
||||
lp.width = size
|
||||
lp.height = size
|
||||
appIconView.layoutParams = lp
|
||||
}
|
||||
|
||||
appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
|
||||
appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
|
||||
return appIconView.contentDescription
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the information needed to display the icon.
|
||||
*
|
||||
* The information will either contain app name and icon of the app playing media, or a default
|
||||
* name and icon if we can't find the app name/icon.
|
||||
*/
|
||||
private fun getIconInfo(appPackageName: String?): IconInfo {
|
||||
if (appPackageName != null) {
|
||||
try {
|
||||
return IconInfo(
|
||||
iconName = context.packageManager.getApplicationInfo(
|
||||
appPackageName, PackageManager.ApplicationInfoFlags.of(0)
|
||||
).loadLabel(context.packageManager).toString(),
|
||||
icon = context.packageManager.getApplicationIcon(appPackageName),
|
||||
isAppIcon = true
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
Log.w(TAG, "Cannot find package $appPackageName", e)
|
||||
}
|
||||
}
|
||||
return IconInfo(
|
||||
iconName = context.getString(R.string.media_output_dialog_unknown_launch_app_name),
|
||||
icon = context.resources.getDrawable(R.drawable.ic_cast).apply {
|
||||
this.setTint(
|
||||
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
|
||||
)
|
||||
},
|
||||
isAppIcon = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
|
||||
// UpdateMediaTapToTransferReceiverDisplayTest
|
||||
private const val WINDOW_TITLE = "Media Transfer Chip View"
|
||||
private val TAG = TemporaryViewDisplayController::class.simpleName!!
|
||||
|
||||
object TemporaryDisplayRemovalReason {
|
||||
const val REASON_TIMEOUT = "TIMEOUT"
|
||||
|
||||
@@ -23,11 +23,11 @@ import com.android.systemui.log.LogBuffer
|
||||
import com.android.systemui.log.LogBufferFactory
|
||||
import com.android.systemui.log.LogcatEchoTracker
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.mock
|
||||
import java.io.PrintWriter
|
||||
import java.io.StringWriter
|
||||
|
||||
@SmallTest
|
||||
class MediaTttLoggerTest : SysuiTestCase() {
|
||||
@@ -43,11 +43,12 @@ class MediaTttLoggerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun logStateChange_bufferHasDeviceTypeTagAndStateNameAndId() {
|
||||
fun logStateChange_bufferHasDeviceTypeTagAndParamInfo() {
|
||||
val stateName = "test state name"
|
||||
val id = "test id"
|
||||
val packageName = "this.is.a.package"
|
||||
|
||||
logger.logStateChange(stateName, id)
|
||||
logger.logStateChange(stateName, id, packageName)
|
||||
|
||||
val stringWriter = StringWriter()
|
||||
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
|
||||
@@ -56,6 +57,7 @@ class MediaTttLoggerTest : SysuiTestCase() {
|
||||
assertThat(actualString).contains(DEVICE_TYPE_TAG)
|
||||
assertThat(actualString).contains(stateName)
|
||||
assertThat(actualString).contains(id)
|
||||
assertThat(actualString).contains(packageName)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,6 +72,19 @@ class MediaTttLoggerTest : SysuiTestCase() {
|
||||
assertThat(actualString).contains(DEVICE_TYPE_TAG)
|
||||
assertThat(actualString).contains(reason)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun logPackageNotFound_bufferHasPackageName() {
|
||||
val packageName = "this.is.a.package"
|
||||
|
||||
logger.logPackageNotFound(packageName)
|
||||
|
||||
val stringWriter = StringWriter()
|
||||
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
|
||||
val actualString = stringWriter.toString()
|
||||
|
||||
assertThat(actualString).contains(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEVICE_TYPE_TAG = "TEST TYPE"
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media.taptotransfer.common
|
||||
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.FrameLayout
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class MediaTttUtilsTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var appIconFromPackageName: Drawable
|
||||
@Mock private lateinit var packageManager: PackageManager
|
||||
@Mock private lateinit var applicationInfo: ApplicationInfo
|
||||
@Mock private lateinit var logger: MediaTttLogger
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
// Set up our package manager to give valid information for [PACKAGE_NAME] only
|
||||
appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
|
||||
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
|
||||
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
|
||||
whenever(
|
||||
packageManager.getApplicationInfo(any(), any<PackageManager.ApplicationInfoFlags>())
|
||||
)
|
||||
.thenThrow(PackageManager.NameNotFoundException())
|
||||
whenever(
|
||||
packageManager.getApplicationInfo(
|
||||
Mockito.eq(PACKAGE_NAME),
|
||||
any<PackageManager.ApplicationInfoFlags>()
|
||||
)
|
||||
)
|
||||
.thenReturn(applicationInfo)
|
||||
context.setMockPackageManager(packageManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
|
||||
val iconInfo =
|
||||
MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isFalse()
|
||||
assertThat(iconInfo.contentDescription)
|
||||
.isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isFalse()
|
||||
assertThat(iconInfo.contentDescription)
|
||||
.isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isTrue()
|
||||
assertThat(iconInfo.drawable).isEqualTo(appIconFromPackageName)
|
||||
assertThat(iconInfo.contentDescription).isEqualTo(APP_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_viewHasIconAndContentDescription() {
|
||||
val view = CachingIconView(context)
|
||||
val icon = context.getDrawable(R.drawable.ic_celebration)!!
|
||||
val contentDescription = "Happy birthday!"
|
||||
|
||||
MediaTttUtils.setIcon(view, icon, contentDescription)
|
||||
|
||||
assertThat(view.drawable).isEqualTo(icon)
|
||||
assertThat(view.contentDescription).isEqualTo(contentDescription)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_iconSizeNull_viewSizeDoesNotChange() {
|
||||
val view = CachingIconView(context)
|
||||
val size = 456
|
||||
view.layoutParams = FrameLayout.LayoutParams(size, size)
|
||||
|
||||
MediaTttUtils.setIcon(view, context.getDrawable(R.drawable.ic_cake)!!, "desc")
|
||||
|
||||
assertThat(view.layoutParams.width).isEqualTo(size)
|
||||
assertThat(view.layoutParams.height).isEqualTo(size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_iconSizeProvided_viewSizeUpdates() {
|
||||
val view = CachingIconView(context)
|
||||
val size = 456
|
||||
view.layoutParams = FrameLayout.LayoutParams(size, size)
|
||||
|
||||
val newSize = 40
|
||||
MediaTttUtils.setIcon(
|
||||
view,
|
||||
context.getDrawable(R.drawable.ic_cake)!!,
|
||||
"desc",
|
||||
iconSize = newSize
|
||||
)
|
||||
|
||||
assertThat(view.layoutParams.width).isEqualTo(newSize)
|
||||
assertThat(view.layoutParams.height).isEqualTo(newSize)
|
||||
}
|
||||
}
|
||||
|
||||
private const val PACKAGE_NAME = "com.android.systemui"
|
||||
private const val APP_NAME = "Fake App Name"
|
||||
@@ -173,37 +173,72 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
verify(logger).logStateChange(any(), any())
|
||||
verify(logger).logStateChange(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_isAppIcon_usesAppIconSize() {
|
||||
controllerReceiver.displayView(getChipReceiverInfo())
|
||||
fun updateView_noOverrides_usesInfoFromAppIcon() {
|
||||
controllerReceiver.displayView(
|
||||
ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride = null)
|
||||
)
|
||||
|
||||
val view = getChipView()
|
||||
assertThat(view.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
|
||||
assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateView_appIconOverride_usesOverride() {
|
||||
val drawableOverride = context.getDrawable(R.drawable.ic_celebration)!!
|
||||
|
||||
controllerReceiver.displayView(
|
||||
ChipReceiverInfo(routeInfo, drawableOverride, appNameOverride = null)
|
||||
)
|
||||
|
||||
val view = getChipView()
|
||||
assertThat(view.getAppIconView().drawable).isEqualTo(drawableOverride)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateView_appNameOverride_usesOverride() {
|
||||
val appNameOverride = "Sweet New App"
|
||||
|
||||
controllerReceiver.displayView(
|
||||
ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride)
|
||||
)
|
||||
|
||||
val view = getChipView()
|
||||
assertThat(view.getAppIconView().contentDescription).isEqualTo(appNameOverride)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateView_isAppIcon_usesAppIconSize() {
|
||||
controllerReceiver.displayView(getChipReceiverInfo(packageName = PACKAGE_NAME))
|
||||
val chipView = getChipView()
|
||||
|
||||
controllerReceiver.setIcon(chipView, PACKAGE_NAME)
|
||||
chipView.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
)
|
||||
|
||||
val expectedSize = controllerReceiver.getIconSize(isAppIcon = true)
|
||||
val expectedSize =
|
||||
context.resources.getDimensionPixelSize(R.dimen.media_ttt_icon_size_receiver)
|
||||
assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize)
|
||||
assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_notAppIcon_usesGenericIconSize() {
|
||||
controllerReceiver.displayView(getChipReceiverInfo())
|
||||
fun updateView_notAppIcon_usesGenericIconSize() {
|
||||
controllerReceiver.displayView(getChipReceiverInfo(packageName = null))
|
||||
val chipView = getChipView()
|
||||
|
||||
controllerReceiver.setIcon(chipView, appPackageName = null)
|
||||
chipView.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
)
|
||||
|
||||
val expectedSize = controllerReceiver.getIconSize(isAppIcon = false)
|
||||
val expectedSize =
|
||||
context.resources.getDimensionPixelSize(R.dimen.media_ttt_generic_icon_size_receiver)
|
||||
assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize)
|
||||
assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize)
|
||||
}
|
||||
@@ -226,8 +261,13 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
|
||||
return viewCaptor.value as ViewGroup
|
||||
}
|
||||
|
||||
private fun getChipReceiverInfo(): ChipReceiverInfo =
|
||||
ChipReceiverInfo(routeInfo, null, null)
|
||||
private fun getChipReceiverInfo(packageName: String?): ChipReceiverInfo {
|
||||
val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
|
||||
.addFeature("feature")
|
||||
.setClientPackageName(packageName)
|
||||
.build()
|
||||
return ChipReceiverInfo(routeInfo, null, null)
|
||||
}
|
||||
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
null
|
||||
)
|
||||
|
||||
verify(logger).logStateChange(any(), any())
|
||||
verify(logger).logStateChange(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -590,12 +590,24 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
|
||||
val state = transferToReceiverTriggered()
|
||||
controllerSender.displayView(state)
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fun transferToReceiverTriggeredThenFarFromReceiver_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToReceiverTriggered())
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||
routeInfo,
|
||||
null
|
||||
)
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverTriggeredThenRemoveView_eventuallyTimesOut() {
|
||||
controllerSender.displayView(transferToReceiverTriggered())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
@@ -613,17 +625,97 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
|
||||
val state = transferToThisDeviceTriggered()
|
||||
controllerSender.displayView(state)
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fun transferToThisDeviceTriggeredThenRemoveView_eventuallyTimesOut() {
|
||||
controllerSender.displayView(transferToThisDeviceTriggered())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceTriggeredThenFarFromReceiver_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToThisDeviceTriggered())
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||
routeInfo,
|
||||
null
|
||||
)
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceededThenRemoveView_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToReceiverSucceeded())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceededThenRemoveView_eventuallyTimesOut() {
|
||||
controllerSender.displayView(transferToReceiverSucceeded())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverSucceededThenFarFromReceiver_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToReceiverSucceeded())
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||
routeInfo,
|
||||
null
|
||||
)
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceSucceededThenRemoveView_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToThisDeviceSucceeded())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceSucceededThenRemoveView_eventuallyTimesOut() {
|
||||
controllerSender.displayView(transferToThisDeviceSucceeded())
|
||||
|
||||
controllerSender.removeView("fakeRemovalReason")
|
||||
fakeClock.advanceTime(TIMEOUT + 1L)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceSucceededThenFarFromReceiver_viewStillDisplayed() {
|
||||
controllerSender.displayView(transferToThisDeviceSucceeded())
|
||||
|
||||
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
|
||||
routeInfo,
|
||||
null
|
||||
)
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
|
||||
private fun ViewGroup.getChipText(): String =
|
||||
|
||||
@@ -17,15 +17,10 @@
|
||||
package com.android.systemui.temporarydisplay
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.PowerManager
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import android.widget.ImageView
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -42,9 +37,7 @@ import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.eq
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.reset
|
||||
import org.mockito.Mockito.verify
|
||||
@@ -58,11 +51,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
|
||||
private lateinit var appIconFromPackageName: Drawable
|
||||
@Mock
|
||||
private lateinit var packageManager: PackageManager
|
||||
@Mock
|
||||
private lateinit var applicationInfo: ApplicationInfo
|
||||
@Mock
|
||||
private lateinit var logger: MediaTttLogger
|
||||
@Mock
|
||||
@@ -78,17 +66,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
|
||||
whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
|
||||
whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
|
||||
whenever(packageManager.getApplicationInfo(
|
||||
any(), any<PackageManager.ApplicationInfoFlags>()
|
||||
)).thenThrow(PackageManager.NameNotFoundException())
|
||||
whenever(packageManager.getApplicationInfo(
|
||||
eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
|
||||
)).thenReturn(applicationInfo)
|
||||
context.setMockPackageManager(packageManager)
|
||||
|
||||
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any()))
|
||||
.thenReturn(TIMEOUT_MS.toInt())
|
||||
|
||||
@@ -229,117 +206,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null)
|
||||
|
||||
assertThat(view.getAppIconView().drawable).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(
|
||||
view, appPackageName = "fakePackageName", appIconDrawableOverride = null
|
||||
)
|
||||
|
||||
assertThat(view.getAppIconView().drawable).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null)
|
||||
|
||||
assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_hasAppIconDrawable_iconIsDrawable() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
val drawable = context.getDrawable(R.drawable.ic_alarm)!!
|
||||
underTest.setIcon(view, PACKAGE_NAME, drawable, null)
|
||||
|
||||
assertThat(view.getAppIconView().drawable).isEqualTo(drawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(view, appPackageName = null, appNameOverride = null)
|
||||
|
||||
assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(
|
||||
view, appPackageName = "fakePackageName", appNameOverride = null
|
||||
)
|
||||
|
||||
assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null)
|
||||
|
||||
assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
val appName = "Override App Name"
|
||||
underTest.setIcon(view, PACKAGE_NAME, null, appName)
|
||||
|
||||
assertThat(view.getAppIconView().contentDescription).isEqualTo(appName)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setIcon_iconSizeMatchesGetIconSize() {
|
||||
underTest.displayView(getState())
|
||||
val view = getView()
|
||||
|
||||
underTest.setIcon(view, PACKAGE_NAME)
|
||||
view.measure(
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
|
||||
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
|
||||
)
|
||||
|
||||
assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
|
||||
assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
|
||||
}
|
||||
|
||||
private fun getState(name: String = "name") = ViewInfo(name)
|
||||
|
||||
private fun getView(): ViewGroup {
|
||||
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
|
||||
verify(windowManager).addView(viewCaptor.capture(), any())
|
||||
return viewCaptor.value as ViewGroup
|
||||
}
|
||||
|
||||
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
|
||||
private fun getConfigurationListener(): ConfigurationListener {
|
||||
val callbackCaptor = argumentCaptor<ConfigurationListener>()
|
||||
verify(configurationController).addCallback(capture(callbackCaptor))
|
||||
@@ -371,7 +239,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
super.updateView(newInfo, currentView)
|
||||
mostRecentViewInfo = newInfo
|
||||
}
|
||||
override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
|
||||
}
|
||||
|
||||
inner class ViewInfo(val name: String) : TemporaryViewInfo {
|
||||
@@ -379,7 +246,4 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
|
||||
}
|
||||
}
|
||||
|
||||
private const val PACKAGE_NAME = "com.android.systemui"
|
||||
private const val APP_NAME = "Fake App Name"
|
||||
private const val TIMEOUT_MS = 10000L
|
||||
private const val ICON_SIZE = 47
|
||||
|
||||
Reference in New Issue
Block a user