• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.systemui.statusbar.policy
17 
18 import android.content.Context
19 import android.content.pm.PackageManager
20 import android.media.MediaRouter
21 import android.media.projection.MediaProjectionInfo
22 import android.text.TextUtils
23 import com.android.systemui.log.core.LogLevel
24 import com.android.systemui.res.R
25 import com.android.systemui.util.Utils
26 
27 /** Represents a specific cast session. */
28 data class CastDevice(
29     val id: String,
30     /** A human-readable name of what is receiving the cast (e.g. "Home Speaker", "Abc App"). */
31     val name: String?,
32     /** An optional description with more information about the cast. */
33     val description: String? = null,
34     val state: CastState,
35     val origin: CastOrigin,
36     /** Optional tag to use as a comparison value between cast sessions. */
37     val tag: Any? = null,
38 ) {
39     val isCasting = state == CastState.Connecting || state == CastState.Connected
40 
41     val shortLogString: String =
42         "CastDevice(id=$id name=$name description=$description state=$state origin=$origin)"
43 
44     companion object {
45         /** Creates a [CastDevice] based on the provided information from MediaRouter. */
toCastDevicenull46         fun MediaRouter.RouteInfo.toCastDevice(context: Context): CastDevice {
47             val state =
48                 when {
49                     statusCode == MediaRouter.RouteInfo.STATUS_CONNECTING -> CastState.Connecting
50                     this.isSelected || statusCode == MediaRouter.RouteInfo.STATUS_CONNECTED ->
51                         CastState.Connected
52                     else -> CastState.Disconnected
53                 }
54             return CastDevice(
55                 id = this.tag.toString(),
56                 name = this.getName(context)?.toString(),
57                 description = this.description?.toString(),
58                 state = state,
59                 tag = this,
60                 origin = CastOrigin.MediaRouter,
61             )
62         }
63 
64         /** Creates a [CastDevice] based on the provided information from MediaProjection. */
MediaProjectionInfonull65         fun MediaProjectionInfo.toCastDevice(
66             context: Context,
67             packageManager: PackageManager,
68             logger: CastControllerLogger,
69         ): CastDevice {
70             return CastDevice(
71                 id = this.packageName,
72                 name = getAppName(this.packageName, packageManager, logger),
73                 description = context.getString(R.string.quick_settings_casting),
74                 state = CastState.Connected,
75                 tag = this,
76                 origin = CastOrigin.MediaProjection,
77             )
78         }
79 
getAppNamenull80         private fun getAppName(
81             packageName: String,
82             packageManager: PackageManager,
83             logger: CastControllerLogger,
84         ): String {
85             if (Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName)) {
86                 return ""
87             }
88             try {
89                 val appInfo = packageManager.getApplicationInfo(packageName, 0)
90                 val label = appInfo.loadLabel(packageManager)
91                 if (!TextUtils.isEmpty(label)) {
92                     return label.toString()
93                 }
94                 logger.log(
95                     "#getAppName",
96                     LogLevel.WARNING,
97                     { str1 = packageName },
98                     { "No label found for package: $str1" },
99                 )
100             } catch (e: PackageManager.NameNotFoundException) {
101                 logger.log(
102                     "#getAppName",
103                     LogLevel.WARNING,
104                     { str1 = packageName },
105                     { "Error getting appName for package=$str1" },
106                     e,
107                 )
108             }
109             return packageName
110         }
111     }
112 
113     enum class CastState {
114         Disconnected,
115         Connecting,
116         Connected,
117     }
118 
119     enum class CastOrigin {
120         /** SysUI found out about this cast device from MediaRouter APIs. */
121         MediaRouter,
122         /** SysUI found out about this cast device from MediaProjection APIs. */
123         MediaProjection,
124     }
125 }
126