Merge "[Chipbar] Always tint the start icon correctly." into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
80821636d9
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.common.shared.model
|
||||
|
||||
import androidx.annotation.AttrRes
|
||||
|
||||
/** Models an icon with a specific tint. */
|
||||
data class TintedIcon(
|
||||
val icon: Icon,
|
||||
@AttrRes val tintAttr: Int?,
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.common.ui.binder
|
||||
|
||||
import android.widget.ImageView
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.common.shared.model.TintedIcon
|
||||
|
||||
object TintedIconViewBinder {
|
||||
/**
|
||||
* Binds the given tinted icon to the view.
|
||||
*
|
||||
* [TintedIcon.tintAttr] will always be applied, meaning that if it is null, then the tint
|
||||
* *will* be reset to null.
|
||||
*/
|
||||
fun bind(
|
||||
tintedIcon: TintedIcon,
|
||||
view: ImageView,
|
||||
) {
|
||||
IconViewBinder.bind(tintedIcon.icon, view)
|
||||
view.imageTintList =
|
||||
if (tintedIcon.tintAttr != null) {
|
||||
Utils.getColorAttr(view.context, tintedIcon.tintAttr)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ package com.android.systemui.media.taptotransfer.common
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.settingslib.Utils
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.common.shared.model.TintedIcon
|
||||
|
||||
/** Utility methods for media tap-to-transfer. */
|
||||
class MediaTttUtils {
|
||||
@@ -33,23 +35,6 @@ class MediaTttUtils {
|
||||
const val WAKE_REASON_SENDER = "MEDIA_TRANSFER_ACTIVATED_SENDER"
|
||||
const val WAKE_REASON_RECEIVER = "MEDIA_TRANSFER_ACTIVATED_RECEIVER"
|
||||
|
||||
/**
|
||||
* Returns the information needed to display the icon in [Icon] form.
|
||||
*
|
||||
* See [getIconInfoFromPackageName].
|
||||
*/
|
||||
fun getIconFromPackageName(
|
||||
context: Context,
|
||||
appPackageName: String?,
|
||||
logger: MediaTttLogger,
|
||||
): Icon {
|
||||
val iconInfo = getIconInfoFromPackageName(context, appPackageName, logger)
|
||||
return Icon.Loaded(
|
||||
iconInfo.drawable,
|
||||
ContentDescription.Loaded(iconInfo.contentDescription)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the information needed to display the icon.
|
||||
*
|
||||
@@ -65,18 +50,22 @@ class MediaTttUtils {
|
||||
logger: MediaTttLogger
|
||||
): IconInfo {
|
||||
if (appPackageName != null) {
|
||||
val packageManager = context.packageManager
|
||||
try {
|
||||
val contentDescription =
|
||||
context.packageManager
|
||||
.getApplicationInfo(
|
||||
appPackageName,
|
||||
PackageManager.ApplicationInfoFlags.of(0)
|
||||
)
|
||||
.loadLabel(context.packageManager)
|
||||
.toString()
|
||||
ContentDescription.Loaded(
|
||||
packageManager
|
||||
.getApplicationInfo(
|
||||
appPackageName,
|
||||
PackageManager.ApplicationInfoFlags.of(0)
|
||||
)
|
||||
.loadLabel(packageManager)
|
||||
.toString()
|
||||
)
|
||||
return IconInfo(
|
||||
contentDescription,
|
||||
drawable = context.packageManager.getApplicationIcon(appPackageName),
|
||||
MediaTttIcon.Loaded(packageManager.getApplicationIcon(appPackageName)),
|
||||
tintAttr = null,
|
||||
isAppIcon = true
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
@@ -84,25 +73,41 @@ class MediaTttUtils {
|
||||
}
|
||||
}
|
||||
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)
|
||||
)
|
||||
},
|
||||
ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name),
|
||||
MediaTttIcon.Resource(R.drawable.ic_cast),
|
||||
tintAttr = android.R.attr.textColorPrimary,
|
||||
isAppIcon = false
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Stores all the information for an icon shown with media TTT. */
|
||||
data class IconInfo(
|
||||
val contentDescription: String,
|
||||
val drawable: Drawable,
|
||||
val contentDescription: ContentDescription,
|
||||
val icon: MediaTttIcon,
|
||||
@AttrRes val tintAttr: Int?,
|
||||
/**
|
||||
* True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
|
||||
*/
|
||||
val isAppIcon: Boolean
|
||||
)
|
||||
) {
|
||||
/** Converts this into a [TintedIcon]. */
|
||||
fun toTintedIcon(): TintedIcon {
|
||||
val iconOutput =
|
||||
when (icon) {
|
||||
is MediaTttIcon.Loaded -> Icon.Loaded(icon.drawable, contentDescription)
|
||||
is MediaTttIcon.Resource -> Icon.Resource(icon.res, contentDescription)
|
||||
}
|
||||
return TintedIcon(iconOutput, tintAttr)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mimics [com.android.systemui.common.shared.model.Icon] but without the content description, since
|
||||
* the content description may need to be overridden.
|
||||
*/
|
||||
sealed interface MediaTttIcon {
|
||||
data class Loaded(val drawable: Drawable) : MediaTttIcon
|
||||
data class Resource(@DrawableRes val res: Int) : MediaTttIcon
|
||||
}
|
||||
|
||||
@@ -33,9 +33,12 @@ import android.view.accessibility.AccessibilityManager
|
||||
import com.android.internal.widget.CachingIconView
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.ui.binder.TintedIconViewBinder
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.media.taptotransfer.MediaTttFlags
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttIcon
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttUtils
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
@@ -161,11 +164,23 @@ open class MediaTttChipControllerReceiver @Inject constructor(
|
||||
}
|
||||
|
||||
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
var iconInfo = MediaTttUtils.getIconInfoFromPackageName(
|
||||
context, newInfo.routeInfo.clientPackageName, logger
|
||||
)
|
||||
val iconDrawable = newInfo.appIconDrawableOverride ?: iconInfo.drawable
|
||||
val iconContentDescription = newInfo.appNameOverride ?: iconInfo.contentDescription
|
||||
|
||||
if (newInfo.appNameOverride != null) {
|
||||
iconInfo = iconInfo.copy(
|
||||
contentDescription = ContentDescription.Loaded(newInfo.appNameOverride.toString())
|
||||
)
|
||||
}
|
||||
|
||||
if (newInfo.appIconDrawableOverride != null) {
|
||||
iconInfo = iconInfo.copy(
|
||||
icon = MediaTttIcon.Loaded(newInfo.appIconDrawableOverride),
|
||||
isAppIcon = true,
|
||||
)
|
||||
}
|
||||
|
||||
val iconPadding =
|
||||
if (iconInfo.isAppIcon) {
|
||||
0
|
||||
@@ -175,8 +190,7 @@ open class MediaTttChipControllerReceiver @Inject constructor(
|
||||
|
||||
val iconView = currentView.getAppIconView()
|
||||
iconView.setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
|
||||
iconView.setImageDrawable(iconDrawable)
|
||||
iconView.contentDescription = iconContentDescription
|
||||
TintedIconViewBinder.bind(iconInfo.toTintedIcon(), iconView)
|
||||
}
|
||||
|
||||
override fun animateViewIn(view: ViewGroup) {
|
||||
|
||||
@@ -153,7 +153,9 @@ constructor(
|
||||
|
||||
return ChipbarInfo(
|
||||
// Display the app's icon as the start icon
|
||||
startIcon = MediaTttUtils.getIconFromPackageName(context, packageName, logger),
|
||||
startIcon =
|
||||
MediaTttUtils.getIconInfoFromPackageName(context, packageName, logger)
|
||||
.toTintedIcon(),
|
||||
text = chipStateSender.getChipTextString(context, otherDeviceName),
|
||||
endItem =
|
||||
when (chipStateSender.endItem) {
|
||||
|
||||
@@ -34,8 +34,8 @@ import com.android.systemui.animation.ViewHierarchyAnimator
|
||||
import com.android.systemui.classifier.FalsingCollector
|
||||
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
|
||||
import com.android.systemui.common.shared.model.Text.Companion.loadText
|
||||
import com.android.systemui.common.ui.binder.IconViewBinder
|
||||
import com.android.systemui.common.ui.binder.TextViewBinder
|
||||
import com.android.systemui.common.ui.binder.TintedIconViewBinder
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Main
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
@@ -121,7 +121,7 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
|
||||
// ---- Start icon ----
|
||||
val iconView = currentView.requireViewById<CachingIconView>(R.id.start_icon)
|
||||
IconViewBinder.bind(newInfo.startIcon, iconView)
|
||||
TintedIconViewBinder.bind(newInfo.startIcon, iconView)
|
||||
|
||||
// ---- Text ----
|
||||
val textView = currentView.requireViewById<TextView>(R.id.text)
|
||||
@@ -159,7 +159,7 @@ open class ChipbarCoordinator @Inject constructor(
|
||||
currentView.requireViewById<ViewGroup>(
|
||||
R.id.chipbar_inner
|
||||
).contentDescription =
|
||||
"${newInfo.startIcon.contentDescription.loadContentDescription(context)} " +
|
||||
"${newInfo.startIcon.icon.contentDescription.loadContentDescription(context)} " +
|
||||
"${newInfo.text.loadText(context)}"
|
||||
|
||||
// ---- Haptics ----
|
||||
|
||||
@@ -18,8 +18,9 @@ package com.android.systemui.temporarydisplay.chipbar
|
||||
|
||||
import android.os.VibrationEffect
|
||||
import android.view.View
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import androidx.annotation.AttrRes
|
||||
import com.android.systemui.common.shared.model.Text
|
||||
import com.android.systemui.common.shared.model.TintedIcon
|
||||
import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||
|
||||
/**
|
||||
@@ -33,7 +34,7 @@ import com.android.systemui.temporarydisplay.TemporaryViewInfo
|
||||
* @property vibrationEffect an optional vibration effect when the chipbar is displayed
|
||||
*/
|
||||
data class ChipbarInfo(
|
||||
val startIcon: Icon,
|
||||
val startIcon: TintedIcon,
|
||||
val text: Text,
|
||||
val endItem: ChipbarEndItem?,
|
||||
val vibrationEffect: VibrationEffect? = null,
|
||||
@@ -41,7 +42,11 @@ data class ChipbarInfo(
|
||||
override val wakeReason: String,
|
||||
override val timeoutMs: Int,
|
||||
override val id: String,
|
||||
) : TemporaryViewInfo()
|
||||
) : TemporaryViewInfo() {
|
||||
companion object {
|
||||
@AttrRes const val DEFAULT_ICON_TINT_ATTR = android.R.attr.textColorPrimary
|
||||
}
|
||||
}
|
||||
|
||||
/** The possible items to display at the end of the chipbar. */
|
||||
sealed class ChipbarEndItem {
|
||||
|
||||
@@ -64,42 +64,15 @@ class MediaTttUtilsTest : SysuiTestCase() {
|
||||
context.setMockPackageManager(packageManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconFromPackageName_nullPackageName_returnsDefault() {
|
||||
val icon = MediaTttUtils.getIconFromPackageName(context, appPackageName = null, logger)
|
||||
|
||||
val expectedDesc =
|
||||
ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name)
|
||||
.loadContentDescription(context)
|
||||
assertThat(icon.contentDescription.loadContentDescription(context)).isEqualTo(expectedDesc)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconFromPackageName_invalidPackageName_returnsDefault() {
|
||||
val icon = MediaTttUtils.getIconFromPackageName(context, "fakePackageName", logger)
|
||||
|
||||
val expectedDesc =
|
||||
ContentDescription.Resource(R.string.media_output_dialog_unknown_launch_app_name)
|
||||
.loadContentDescription(context)
|
||||
assertThat(icon.contentDescription.loadContentDescription(context)).isEqualTo(expectedDesc)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconFromPackageName_validPackageName_returnsAppInfo() {
|
||||
val icon = MediaTttUtils.getIconFromPackageName(context, PACKAGE_NAME, logger)
|
||||
|
||||
assertThat(icon)
|
||||
.isEqualTo(Icon.Loaded(appIconFromPackageName, ContentDescription.Loaded(APP_NAME)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
|
||||
val iconInfo =
|
||||
MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isFalse()
|
||||
assertThat(iconInfo.contentDescription)
|
||||
assertThat(iconInfo.contentDescription.loadContentDescription(context))
|
||||
.isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
|
||||
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -107,8 +80,9 @@ class MediaTttUtilsTest : SysuiTestCase() {
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isFalse()
|
||||
assertThat(iconInfo.contentDescription)
|
||||
assertThat(iconInfo.contentDescription.loadContentDescription(context))
|
||||
.isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
|
||||
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Resource(R.drawable.ic_cast))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,8 +90,48 @@ class MediaTttUtilsTest : SysuiTestCase() {
|
||||
val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
|
||||
|
||||
assertThat(iconInfo.isAppIcon).isTrue()
|
||||
assertThat(iconInfo.drawable).isEqualTo(appIconFromPackageName)
|
||||
assertThat(iconInfo.contentDescription).isEqualTo(APP_NAME)
|
||||
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
|
||||
assertThat(iconInfo.contentDescription.loadContentDescription(context)).isEqualTo(APP_NAME)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iconInfo_toTintedIcon_loaded() {
|
||||
val contentDescription = ContentDescription.Loaded("test")
|
||||
val drawable = context.getDrawable(R.drawable.ic_cake)!!
|
||||
val tintAttr = android.R.attr.textColorTertiary
|
||||
|
||||
val iconInfo =
|
||||
IconInfo(
|
||||
contentDescription,
|
||||
MediaTttIcon.Loaded(drawable),
|
||||
tintAttr,
|
||||
isAppIcon = false,
|
||||
)
|
||||
|
||||
val tinted = iconInfo.toTintedIcon()
|
||||
|
||||
assertThat(tinted.icon).isEqualTo(Icon.Loaded(drawable, contentDescription))
|
||||
assertThat(tinted.tintAttr).isEqualTo(tintAttr)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iconInfo_toTintedIcon_resource() {
|
||||
val contentDescription = ContentDescription.Loaded("test")
|
||||
val drawableRes = R.drawable.ic_cake
|
||||
val tintAttr = android.R.attr.textColorTertiary
|
||||
|
||||
val iconInfo =
|
||||
IconInfo(
|
||||
contentDescription,
|
||||
MediaTttIcon.Resource(drawableRes),
|
||||
tintAttr,
|
||||
isAppIcon = false
|
||||
)
|
||||
|
||||
val tinted = iconInfo.toTintedIcon()
|
||||
|
||||
assertThat(tinted.icon).isEqualTo(Icon.Resource(drawableRes, contentDescription))
|
||||
assertThat(tinted.tintAttr).isEqualTo(tintAttr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.android.systemui.common.shared.model.ContentDescription
|
||||
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.common.shared.model.Text
|
||||
import com.android.systemui.common.shared.model.TintedIcon
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.VibratorHelper
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
@@ -370,7 +371,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
|
||||
vibrationEffect: VibrationEffect? = null,
|
||||
): ChipbarInfo {
|
||||
return ChipbarInfo(
|
||||
startIcon,
|
||||
TintedIcon(startIcon, tintAttr = null),
|
||||
text,
|
||||
endItem,
|
||||
vibrationEffect,
|
||||
|
||||
Reference in New Issue
Block a user