blob: dbeff4d7ea0e0c5fa50b5a581bdf225089b212ce [file] [log] [blame]
/*
* Copyright (C) 2018 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.car.carlauncher;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.content.pm.CarPackageManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Process;
import android.service.media.MediaBrowserService;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Util class that contains helper method used by app launcher classes.
*/
class AppLauncherUtils {
private static final String TAG = "AppLauncherUtils";
private AppLauncherUtils() {
}
/**
* Comparator for {@link AppMetaData} that sorts the list
* by the "displayName" property in ascending order.
*/
static final Comparator<AppMetaData> ALPHABETICAL_COMPARATOR = Comparator
.comparing(AppMetaData::getDisplayName, String::compareToIgnoreCase);
/**
* Helper method that launches the app given the app's AppMetaData.
*
* @param app the requesting app's AppMetaData
*/
static void launchApp(Context context, AppMetaData app) {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(context.getDisplayId());
context.startActivity(app.getMainLaunchIntent(), options.toBundle());
}
/** Bundles application and services info. */
static class LauncherAppsInfo {
/** Map of all apps' metadata keyed by package name. */
private final Map<String, AppMetaData> mApplications;
/** Map of all the media services keyed by package name. */
private final Map<String, ResolveInfo> mMediaServices;
LauncherAppsInfo(@NonNull Map<String, AppMetaData> apps,
@NonNull Map<String, ResolveInfo> mediaServices) {
mApplications = apps;
mMediaServices = mediaServices;
}
/** Returns true if all maps are empty. */
boolean isEmpty() {
return mApplications.isEmpty() && mMediaServices.isEmpty();
}
/** Returns whether the given package name is a media service. */
boolean isMediaService(String packageName) {
return mMediaServices.containsKey(packageName);
}
/** Returns the {@link AppMetaData} for the given package name. */
@Nullable
AppMetaData getAppMetaData(String packageName) {
return mApplications.get(packageName);
}
/** Returns a new list of the applications' {@link AppMetaData}. */
@NonNull
List<AppMetaData> getApplicationsList() {
return new ArrayList<>(mApplications.values());
}
}
private final static LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
Collections.emptyMap(), Collections.emptyMap());
/**
* Gets all the apps that we want to see in the launcher in unsorted order. Includes media
* services without launcher activities.
*
* @param blackList A (possibly empty) list of apps to hide
* @param launcherApps The {@link LauncherApps} system service
* @param carPackageManager The {@link CarPackageManager} system service
* @param packageManager The {@link PackageManager} system service
* @return a new {@link LauncherAppsInfo}
*/
@NonNull
static LauncherAppsInfo getAllLauncherApps(
@NonNull Set<String> blackList,
LauncherApps launcherApps,
CarPackageManager carPackageManager,
PackageManager packageManager) {
if (launcherApps == null || carPackageManager == null || packageManager == null) {
return EMPTY_APPS_INFO;
}
List<ResolveInfo> mediaServices = packageManager.queryIntentServices(
new Intent(MediaBrowserService.SERVICE_INTERFACE),
PackageManager.GET_RESOLVED_FILTER);
List<LauncherActivityInfo> availableActivities =
launcherApps.getActivityList(null, Process.myUserHandle());
Map<String, AppMetaData> apps = new HashMap<>(
mediaServices.size() + availableActivities.size());
Map<String, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
// Process media services
for (ResolveInfo info : mediaServices) {
String packageName = info.serviceInfo.packageName;
mediaServicesMap.put(packageName, info);
if (shouldAdd(packageName, apps, blackList)) {
final boolean isDistractionOptimized = true;
Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
intent.putExtra(Car.CAR_EXTRA_MEDIA_PACKAGE, packageName);
AppMetaData appMetaData = new AppMetaData(
info.serviceInfo.loadLabel(packageManager),
packageName,
info.serviceInfo.loadIcon(packageManager),
isDistractionOptimized,
intent,
packageManager.getLaunchIntentForPackage(packageName));
apps.put(packageName, appMetaData);
}
}
// Process activities
for (LauncherActivityInfo info : availableActivities) {
String packageName = info.getComponentName().getPackageName();
if (shouldAdd(packageName, apps, blackList)) {
boolean isDistractionOptimized =
isActivityDistractionOptimized(carPackageManager, packageName,
info.getName());
AppMetaData appMetaData = new AppMetaData(
info.getLabel(),
packageName,
info.getBadgedIcon(0),
isDistractionOptimized,
packageManager.getLaunchIntentForPackage(packageName),
null);
apps.put(packageName, appMetaData);
}
}
return new LauncherAppsInfo(apps, mediaServicesMap);
}
private static boolean shouldAdd(String packageName, Map<String, AppMetaData> apps,
@NonNull Set<String> blackList) {
return !apps.containsKey(packageName) && !blackList.contains(packageName);
}
/**
* Gets if an activity is distraction optimized.
*
* @param carPackageManager The {@link CarPackageManager} system service
* @param packageName The package name of the app
* @param activityName The requested activity name
* @return true if the supplied activity is distraction optimized
*/
static boolean isActivityDistractionOptimized(
CarPackageManager carPackageManager, String packageName, String activityName) {
boolean isDistractionOptimized = false;
// try getting distraction optimization info
try {
if (carPackageManager != null) {
isDistractionOptimized =
carPackageManager.isActivityDistractionOptimized(packageName, activityName);
}
} catch (CarNotConnectedException e) {
Log.e(TAG, "Car not connected when getting DO info", e);
}
return isDistractionOptimized;
}
}