1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.media.taptotransfer.common
18 
19 import android.content.Context
20 import android.content.pm.PackageManager
21 import android.graphics.drawable.Drawable
22 import androidx.annotation.AttrRes
23 import androidx.annotation.DrawableRes
24 import com.android.systemui.res.R
25 import com.android.systemui.common.shared.model.ContentDescription
26 import com.android.systemui.common.shared.model.Icon
27 import com.android.systemui.common.shared.model.TintedIcon
28 import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo.Companion.DEFAULT_ICON_TINT
29 
30 /** Utility methods for media tap-to-transfer. */
31 class MediaTttUtils {
32     companion object {
33         const val WINDOW_TITLE_SENDER = "Media Transfer Chip View (Sender)"
34         const val WINDOW_TITLE_RECEIVER = "Media Transfer Chip View (Receiver)"
35 
36         const val WAKE_REASON_SENDER = "MEDIA_TRANSFER_ACTIVATED_SENDER"
37         const val WAKE_REASON_RECEIVER = "MEDIA_TRANSFER_ACTIVATED_RECEIVER"
38 
39         /**
40          * Returns the information needed to display the icon.
41          *
42          * The information will either contain app name and icon of the app playing media, or a
43          * default name and icon if we can't find the app name/icon.
44          *
45          * @param appPackageName the package name of the app playing the media.
46          * @param onPackageNotFoundException a function run if a
47          *   [PackageManager.NameNotFoundException] occurs.
48          * @param isReceiver indicates whether the icon is displayed in a receiver view.
49          */
getIconInfoFromPackageNamenull50         fun getIconInfoFromPackageName(
51             context: Context,
52             appPackageName: String?,
53             isReceiver: Boolean,
54             onPackageNotFoundException: () -> Unit,
55         ): IconInfo {
56             if (appPackageName != null) {
57                 val packageManager = context.packageManager
58                 try {
59                     val appName =
60                         packageManager
61                             .getApplicationInfo(
62                                 appPackageName,
63                                 PackageManager.ApplicationInfoFlags.of(0),
64                             )
65                             .loadLabel(packageManager)
66                             .toString()
67                     val contentDescription =
68                         if (isReceiver) {
69                             ContentDescription.Loaded(
70                                 context.getString(
71                                     R.string
72                                         .media_transfer_receiver_content_description_with_app_name,
73                                     appName
74                                 )
75                             )
76                         } else {
77                             ContentDescription.Loaded(appName)
78                         }
79                     return IconInfo(
80                         contentDescription,
81                         MediaTttIcon.Loaded(packageManager.getApplicationIcon(appPackageName)),
82                         tint = null,
83                         isAppIcon = true
84                     )
85                 } catch (e: PackageManager.NameNotFoundException) {
86                     onPackageNotFoundException.invoke()
87                 }
88             }
89             return IconInfo(
90                 if (isReceiver) {
91                     ContentDescription.Resource(
92                         R.string.media_transfer_receiver_content_description_unknown_app
93                     )
94                 } else {
95                     ContentDescription.Resource(
96                         R.string.media_output_dialog_unknown_launch_app_name
97                     )
98                 },
99                 MediaTttIcon.Resource(R.drawable.ic_cast),
100                 tint = DEFAULT_ICON_TINT,
101                 isAppIcon = false
102             )
103         }
104     }
105 }
106 
107 /** Stores all the information for an icon shown with media TTT. */
108 data class IconInfo(
109     val contentDescription: ContentDescription,
110     val icon: MediaTttIcon,
111     @AttrRes val tint: Int?,
112     /**
113      * True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
114      */
115     val isAppIcon: Boolean
116 ) {
117     /** Converts this into a [TintedIcon]. */
toTintedIconnull118     fun toTintedIcon(): TintedIcon {
119         val iconOutput =
120             when (icon) {
121                 is MediaTttIcon.Loaded -> Icon.Loaded(icon.drawable, contentDescription)
122                 is MediaTttIcon.Resource -> Icon.Resource(icon.res, contentDescription)
123             }
124         return TintedIcon(iconOutput, tint)
125     }
126 }
127 
128 /**
129  * Mimics [com.android.systemui.common.shared.model.Icon] but without the content description, since
130  * the content description may need to be overridden.
131  */
132 sealed interface MediaTttIcon {
133     data class Loaded(val drawable: Drawable) : MediaTttIcon
134     data class Resource(@DrawableRes val res: Int) : MediaTttIcon
135 }
136