PictureInPictureSettings.java revision 947a673f74a2dc070bd860aa6f50c05f001adbe5
1/* 2 * Copyright (C) 2017 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 */ 16package com.android.settings.applications.appinfo; 17 18import static android.content.pm.PackageManager.GET_ACTIVITIES; 19 20import android.annotation.Nullable; 21import android.content.Context; 22import android.content.pm.ActivityInfo; 23import android.content.pm.ApplicationInfo; 24import android.content.pm.PackageInfo; 25import android.content.pm.PackageManager; 26import android.content.pm.UserInfo; 27import android.os.Bundle; 28import android.os.UserHandle; 29import android.os.UserManager; 30import android.support.v7.preference.Preference; 31import android.support.v7.preference.Preference.OnPreferenceClickListener; 32import android.support.v7.preference.PreferenceScreen; 33import android.util.IconDrawableFactory; 34import android.util.Pair; 35import android.view.View; 36 37import com.android.internal.annotations.VisibleForTesting; 38import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 39import com.android.settings.R; 40import com.android.settings.applications.AppInfoBase; 41import com.android.settings.notification.EmptyTextSettings; 42import com.android.settings.widget.AppPreference; 43import com.android.settingslib.wrapper.PackageManagerWrapper; 44 45import java.text.Collator; 46import java.util.ArrayList; 47import java.util.Collections; 48import java.util.Comparator; 49import java.util.List; 50 51public class PictureInPictureSettings extends EmptyTextSettings { 52 53 private static final String TAG = PictureInPictureSettings.class.getSimpleName(); 54 @VisibleForTesting 55 static final List<String> IGNORE_PACKAGE_LIST = new ArrayList<>(); 56 static { 57 IGNORE_PACKAGE_LIST.add("com.android.systemui"); 58 } 59 60 /** 61 * Comparator by name, then user id. 62 * {@see PackageItemInfo#DisplayNameComparator} 63 */ 64 static class AppComparator implements Comparator<Pair<ApplicationInfo, Integer>> { 65 66 private final Collator mCollator = Collator.getInstance(); 67 private final PackageManager mPm; 68 69 public AppComparator(PackageManager pm) { 70 mPm = pm; 71 } 72 73 public final int compare(Pair<ApplicationInfo, Integer> a, 74 Pair<ApplicationInfo, Integer> b) { 75 CharSequence sa = a.first.loadLabel(mPm); 76 if (sa == null) sa = a.first.name; 77 CharSequence sb = b.first.loadLabel(mPm); 78 if (sb == null) sb = b.first.name; 79 int nameCmp = mCollator.compare(sa.toString(), sb.toString()); 80 if (nameCmp != 0) { 81 return nameCmp; 82 } else { 83 return a.second - b.second; 84 } 85 } 86 } 87 88 private Context mContext; 89 private PackageManagerWrapper mPackageManager; 90 private UserManager mUserManager; 91 private IconDrawableFactory mIconDrawableFactory; 92 93 /** 94 * @return true if the package has any activities that declare that they support 95 * picture-in-picture. 96 */ 97 98 public static boolean checkPackageHasPictureInPictureActivities(String packageName, 99 ActivityInfo[] activities) { 100 // Skip if it's in the ignored list 101 if (IGNORE_PACKAGE_LIST.contains(packageName)) { 102 return false; 103 } 104 105 // Iterate through all the activities and check if it is resizeable and supports 106 // picture-in-picture 107 if (activities != null) { 108 for (int i = activities.length - 1; i >= 0; i--) { 109 if (activities[i].supportsPictureInPicture()) { 110 return true; 111 } 112 } 113 } 114 return false; 115 } 116 117 public PictureInPictureSettings() { 118 // Do nothing 119 } 120 121 public PictureInPictureSettings(PackageManagerWrapper pm, UserManager um) { 122 mPackageManager = pm; 123 mUserManager = um; 124 } 125 126 @Override 127 public void onCreate(Bundle icicle) { 128 super.onCreate(icicle); 129 130 mContext = getActivity(); 131 mPackageManager = new PackageManagerWrapper(mContext.getPackageManager()); 132 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 133 mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); 134 } 135 136 @Override 137 public void onResume() { 138 super.onResume(); 139 140 // Clear the prefs 141 final PreferenceScreen screen = getPreferenceScreen(); 142 screen.removeAll(); 143 144 // Fetch the set of applications for each profile which have at least one activity that 145 // declare that they support picture-in-picture 146 final PackageManager pm = mPackageManager.getPackageManager(); 147 final ArrayList<Pair<ApplicationInfo, Integer>> pipApps = 148 collectPipApps(UserHandle.myUserId()); 149 Collections.sort(pipApps, new AppComparator(pm)); 150 151 // Rebuild the list of prefs 152 final Context prefContext = getPrefContext(); 153 for (final Pair<ApplicationInfo, Integer> appData : pipApps) { 154 final ApplicationInfo appInfo = appData.first; 155 final int userId = appData.second; 156 final UserHandle user = UserHandle.of(userId); 157 final String packageName = appInfo.packageName; 158 final CharSequence label = appInfo.loadLabel(pm); 159 160 final Preference pref = new AppPreference(prefContext); 161 pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId)); 162 pref.setTitle(pm.getUserBadgedLabel(label, user)); 163 pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext, 164 appInfo.uid, packageName)); 165 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 166 @Override 167 public boolean onPreferenceClick(Preference preference) { 168 AppInfoBase.startAppInfoFragment(PictureInPictureDetails.class, 169 R.string.picture_in_picture_app_detail_title, packageName, appInfo.uid, 170 PictureInPictureSettings.this, -1, getMetricsCategory()); 171 return true; 172 } 173 }); 174 screen.addPreference(pref); 175 } 176 } 177 178 @Override 179 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 180 super.onViewCreated(view, savedInstanceState); 181 setEmptyText(R.string.picture_in_picture_empty_text); 182 } 183 184 @Override 185 protected int getPreferenceScreenResId() { 186 return R.xml.picture_in_picture_settings; 187 } 188 189 @Override 190 public int getMetricsCategory() { 191 return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE; 192 } 193 194 /** 195 * @return the list of applications for the given user and all their profiles that have 196 * activities which support PiP. 197 */ 198 ArrayList<Pair<ApplicationInfo, Integer>> collectPipApps(int userId) { 199 final ArrayList<Pair<ApplicationInfo, Integer>> pipApps = new ArrayList<>(); 200 final ArrayList<Integer> userIds = new ArrayList<>(); 201 for (UserInfo user : mUserManager.getProfiles(userId)) { 202 userIds.add(user.id); 203 } 204 205 for (int id : userIds) { 206 final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser( 207 GET_ACTIVITIES, id); 208 for (PackageInfo packageInfo : installedPackages) { 209 if (checkPackageHasPictureInPictureActivities(packageInfo.packageName, 210 packageInfo.activities)) { 211 pipApps.add(new Pair<>(packageInfo.applicationInfo, id)); 212 } 213 } 214 } 215 return pipApps; 216 } 217} 218