1 /*
<lambda>null2  * 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 
17 package com.android.car.docklib.media
18 
19 import android.app.ActivityManager
20 import android.car.media.CarMediaIntents
21 import android.content.ComponentName
22 import android.content.Intent
23 import android.content.pm.PackageManager
24 import android.content.pm.ResolveInfo
25 import android.os.Build
26 import android.service.media.MediaBrowserService
27 import android.util.Log
28 import androidx.annotation.VisibleForTesting
29 import java.util.stream.Collectors
30 
31 class MediaUtils {
32     companion object {
33         private const val TAG = "MediaUtils"
34         private val DEBUG = Build.isDebuggable()
35 
36         @VisibleForTesting
37         val CAR_MEDIA_ACTIVITY = ComponentName(
38             "com.android.car.media",
39             "com.android.car.media.MediaActivity"
40         )
41 
42         @VisibleForTesting
43         const val CAR_MEDIA_DATA_SCHEME = "custom"
44 
45         fun getMediaComponentName(taskInfo: ActivityManager.RunningTaskInfo): ComponentName? {
46             val data = taskInfo.baseIntent.data
47             if (data == null) {
48                 if (DEBUG) Log.d(TAG, "No data attached to the base intent")
49                 return null
50             }
51             if (CAR_MEDIA_DATA_SCHEME != data.scheme) {
52                 if (DEBUG) Log.d(TAG, "Data scheme doesn't match")
53                 return null
54             }
55             // should drop the first backslash that is part of the schemeSpecificPart
56             val ssp = data.schemeSpecificPart
57             val mediaComponentString = if (ssp.startsWith("/")) ssp.drop(1) else ssp
58             val mediaComponent = ComponentName.unflattenFromString(mediaComponentString)
59             if (DEBUG) Log.d(TAG, "Media component found: $mediaComponent")
60             return mediaComponent
61         }
62 
63         fun isMediaComponent(component: ComponentName?) = component == CAR_MEDIA_ACTIVITY
64 
65         fun createLaunchIntent(componentName: ComponentName) =
66             Intent(CarMediaIntents.ACTION_MEDIA_TEMPLATE)
67                 .putExtra(CarMediaIntents.EXTRA_MEDIA_COMPONENT, componentName.flattenToString())
68 
69         fun fetchMediaServiceComponents(
70             packageManager: PackageManager,
71             packageName: String? = null
72         ): MutableSet<ComponentName> {
73             val intent = Intent(MediaBrowserService.SERVICE_INTERFACE)
74             if (packageName != null) intent.setPackage(packageName)
75             return packageManager.queryIntentServices(
76                 intent,
77                 PackageManager.GET_RESOLVED_FILTER
78             ).stream()
79                 .map { resolveInfo: ResolveInfo -> resolveInfo.serviceInfo.componentName }
80                 .collect(Collectors.toSet())
81         }
82     }
83 }
84