Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 83382685 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[Chipbar] Always tint the start icon correctly.

This creates a TintedIcon & TintedIconViewBinder and forces all chipbar
clients to provide the tint.

Fixes: 260954564
Test: active unlock chipbar watch icon has correct tint in light and
dark mode
Test: media ttt chipbar with default icon and app icon has correct tint
in light and dark mode
Test: media ttt receiver default icon has correct tint in light and dark
mode
Test: atest MediaTttUtilsTest MediaTttChipControllerReceiverTest
ChipbarCoordinatorTest

Change-Id: Ic5d0a97a41b78c851da78d0b159011b27086f8c6
parent 1322828e
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
/*
 * 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?,
)
+42 −0
Original line number Diff line number Diff line
/*
 * 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
            }
    }
}
+42 −37
Original line number Diff line number Diff line
@@ -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
                        ContentDescription.Loaded(
                            packageManager
                                .getApplicationInfo(
                                    appPackageName,
                                    PackageManager.ApplicationInfoFlags.of(0)
                                )
                            .loadLabel(context.packageManager)
                                .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
}
+19 −5
Original line number Diff line number Diff line
@@ -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) {
+3 −1
Original line number Diff line number Diff line
@@ -138,7 +138,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) {
Loading