• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.carlauncher;
18 
19 import android.annotation.Nullable;
20 import android.app.ActivityOptions;
21 import android.car.Car;
22 import android.car.CarNotConnectedException;
23 import android.car.content.pm.CarPackageManager;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.LauncherActivityInfo;
27 import android.content.pm.LauncherApps;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.os.Process;
31 import android.service.media.MediaBrowserService;
32 import android.util.Log;
33 
34 import androidx.annotation.NonNull;
35 
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.HashMap;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 
44 /**
45  * Util class that contains helper method used by app launcher classes.
46  */
47 class AppLauncherUtils {
48 
49     private static final String TAG = "AppLauncherUtils";
50 
AppLauncherUtils()51     private AppLauncherUtils() {
52     }
53 
54     /**
55      * Comparator for {@link AppMetaData} that sorts the list
56      * by the "displayName" property in ascending order.
57      */
58     static final Comparator<AppMetaData> ALPHABETICAL_COMPARATOR = Comparator
59             .comparing(AppMetaData::getDisplayName, String::compareToIgnoreCase);
60 
61     /**
62      * Helper method that launches the app given the app's AppMetaData.
63      *
64      * @param app the requesting app's AppMetaData
65      */
launchApp(Context context, AppMetaData app)66     static void launchApp(Context context, AppMetaData app) {
67         ActivityOptions options = ActivityOptions.makeBasic();
68         options.setLaunchDisplayId(context.getDisplayId());
69         context.startActivity(app.getMainLaunchIntent(), options.toBundle());
70     }
71 
72     /** Bundles application and services info. */
73     static class LauncherAppsInfo {
74         /** Map of all apps' metadata keyed by package name. */
75         private final Map<String, AppMetaData> mApplications;
76 
77         /** Map of all the media services keyed by package name. */
78         private final Map<String, ResolveInfo> mMediaServices;
79 
LauncherAppsInfo(@onNull Map<String, AppMetaData> apps, @NonNull Map<String, ResolveInfo> mediaServices)80         LauncherAppsInfo(@NonNull Map<String, AppMetaData> apps,
81                 @NonNull Map<String, ResolveInfo> mediaServices) {
82             mApplications = apps;
83             mMediaServices = mediaServices;
84         }
85 
86         /** Returns true if all maps are empty. */
isEmpty()87         boolean isEmpty() {
88             return mApplications.isEmpty() && mMediaServices.isEmpty();
89         }
90 
91         /** Returns whether the given package name is a media service. */
isMediaService(String packageName)92         boolean isMediaService(String packageName) {
93             return mMediaServices.containsKey(packageName);
94         }
95 
96         /** Returns the {@link AppMetaData} for the given package name. */
97         @Nullable
getAppMetaData(String packageName)98         AppMetaData getAppMetaData(String packageName) {
99             return mApplications.get(packageName);
100         }
101 
102         /** Returns a new list of the applications' {@link AppMetaData}. */
103         @NonNull
getApplicationsList()104         List<AppMetaData> getApplicationsList() {
105             return new ArrayList<>(mApplications.values());
106         }
107     }
108 
109     private final static LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
110             Collections.emptyMap(), Collections.emptyMap());
111 
112     /**
113      * Gets all the apps that we want to see in the launcher in unsorted order. Includes media
114      * services without launcher activities.
115      *
116      * @param blackList         A (possibly empty) list of apps to hide
117      * @param launcherApps      The {@link LauncherApps} system service
118      * @param carPackageManager The {@link CarPackageManager} system service
119      * @param packageManager    The {@link PackageManager} system service
120      * @return a new {@link LauncherAppsInfo}
121      */
122     @NonNull
getAllLauncherApps( @onNull Set<String> blackList, LauncherApps launcherApps, CarPackageManager carPackageManager, PackageManager packageManager)123     static LauncherAppsInfo getAllLauncherApps(
124             @NonNull Set<String> blackList,
125             LauncherApps launcherApps,
126             CarPackageManager carPackageManager,
127             PackageManager packageManager) {
128 
129         if (launcherApps == null || carPackageManager == null || packageManager == null) {
130             return EMPTY_APPS_INFO;
131         }
132 
133         List<ResolveInfo> mediaServices = packageManager.queryIntentServices(
134                 new Intent(MediaBrowserService.SERVICE_INTERFACE),
135                 PackageManager.GET_RESOLVED_FILTER);
136         List<LauncherActivityInfo> availableActivities =
137                 launcherApps.getActivityList(null, Process.myUserHandle());
138 
139         Map<String, AppMetaData> apps = new HashMap<>(
140                 mediaServices.size() + availableActivities.size());
141         Map<String, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
142 
143         // Process media services
144         for (ResolveInfo info : mediaServices) {
145             String packageName = info.serviceInfo.packageName;
146             mediaServicesMap.put(packageName, info);
147             if (shouldAdd(packageName, apps, blackList)) {
148                 final boolean isDistractionOptimized = true;
149 
150                 Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
151                 intent.putExtra(Car.CAR_EXTRA_MEDIA_PACKAGE, packageName);
152 
153                 AppMetaData appMetaData = new AppMetaData(
154                         info.serviceInfo.loadLabel(packageManager),
155                         packageName,
156                         info.serviceInfo.loadIcon(packageManager),
157                         isDistractionOptimized,
158                         intent,
159                         packageManager.getLaunchIntentForPackage(packageName));
160                 apps.put(packageName, appMetaData);
161             }
162         }
163 
164         // Process activities
165         for (LauncherActivityInfo info : availableActivities) {
166             String packageName = info.getComponentName().getPackageName();
167             if (shouldAdd(packageName, apps, blackList)) {
168                 boolean isDistractionOptimized =
169                         isActivityDistractionOptimized(carPackageManager, packageName,
170                                 info.getName());
171 
172                 AppMetaData appMetaData = new AppMetaData(
173                         info.getLabel(),
174                         packageName,
175                         info.getBadgedIcon(0),
176                         isDistractionOptimized,
177                         packageManager.getLaunchIntentForPackage(packageName),
178                         null);
179                 apps.put(packageName, appMetaData);
180             }
181         }
182 
183         return new LauncherAppsInfo(apps, mediaServicesMap);
184     }
185 
shouldAdd(String packageName, Map<String, AppMetaData> apps, @NonNull Set<String> blackList)186     private static boolean shouldAdd(String packageName, Map<String, AppMetaData> apps,
187             @NonNull Set<String> blackList) {
188         return !apps.containsKey(packageName) && !blackList.contains(packageName);
189     }
190 
191     /**
192      * Gets if an activity is distraction optimized.
193      *
194      * @param carPackageManager The {@link CarPackageManager} system service
195      * @param packageName       The package name of the app
196      * @param activityName      The requested activity name
197      * @return true if the supplied activity is distraction optimized
198      */
isActivityDistractionOptimized( CarPackageManager carPackageManager, String packageName, String activityName)199     static boolean isActivityDistractionOptimized(
200             CarPackageManager carPackageManager, String packageName, String activityName) {
201         boolean isDistractionOptimized = false;
202         // try getting distraction optimization info
203         try {
204             if (carPackageManager != null) {
205                 isDistractionOptimized =
206                         carPackageManager.isActivityDistractionOptimized(packageName, activityName);
207             }
208         } catch (CarNotConnectedException e) {
209             Log.e(TAG, "Car not connected when getting DO info", e);
210         }
211         return isDistractionOptimized;
212     }
213 }
214