| /* |
| * Copyright (C) 2017 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.settings.applications; |
| |
| import static android.content.pm.PackageManager.GET_ACTIVITIES; |
| |
| import android.annotation.Nullable; |
| import android.content.Context; |
| import android.content.pm.ActivityInfo; |
| import android.content.pm.ApplicationInfo; |
| import android.content.pm.PackageInfo; |
| import android.content.pm.PackageManager; |
| import android.content.pm.UserInfo; |
| import android.os.Bundle; |
| import android.os.UserHandle; |
| import android.os.UserManager; |
| import android.support.v7.preference.Preference; |
| import android.support.v7.preference.Preference.OnPreferenceClickListener; |
| import android.support.v7.preference.PreferenceScreen; |
| import android.util.IconDrawableFactory; |
| import android.util.Pair; |
| import android.view.View; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
| import com.android.settings.R; |
| import com.android.settings.notification.EmptyTextSettings; |
| import com.android.settings.wrapper.ActivityInfoWrapper; |
| import com.android.settings.wrapper.UserManagerWrapper; |
| import com.android.settingslib.wrapper.PackageManagerWrapper; |
| |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| public class PictureInPictureSettings extends EmptyTextSettings { |
| |
| private static final String TAG = PictureInPictureSettings.class.getSimpleName(); |
| @VisibleForTesting |
| static final List<String> IGNORE_PACKAGE_LIST = new ArrayList<>(); |
| static { |
| IGNORE_PACKAGE_LIST.add("com.android.systemui"); |
| } |
| |
| /** |
| * Comparator by name, then user id. |
| * {@see PackageItemInfo#DisplayNameComparator} |
| */ |
| static class AppComparator implements Comparator<Pair<ApplicationInfo, Integer>> { |
| |
| private final Collator mCollator = Collator.getInstance(); |
| private final PackageManager mPm; |
| |
| public AppComparator(PackageManager pm) { |
| mPm = pm; |
| } |
| |
| public final int compare(Pair<ApplicationInfo, Integer> a, |
| Pair<ApplicationInfo, Integer> b) { |
| CharSequence sa = a.first.loadLabel(mPm); |
| if (sa == null) sa = a.first.name; |
| CharSequence sb = b.first.loadLabel(mPm); |
| if (sb == null) sb = b.first.name; |
| int nameCmp = mCollator.compare(sa.toString(), sb.toString()); |
| if (nameCmp != 0) { |
| return nameCmp; |
| } else { |
| return a.second - b.second; |
| } |
| } |
| } |
| |
| private Context mContext; |
| private PackageManagerWrapper mPackageManager; |
| private UserManagerWrapper mUserManager; |
| private IconDrawableFactory mIconDrawableFactory; |
| |
| /** |
| * @return true if the package has any activities that declare that they support |
| * picture-in-picture. |
| */ |
| static boolean checkPackageHasPictureInPictureActivities(String packageName, |
| ActivityInfo[] activities) { |
| ActivityInfoWrapper[] wrappedActivities = null; |
| if (activities != null) { |
| wrappedActivities = new ActivityInfoWrapper[activities.length]; |
| for (int i = 0; i < activities.length; i++) { |
| wrappedActivities[i] = new ActivityInfoWrapper(activities[i]); |
| } |
| } |
| return checkPackageHasPictureInPictureActivities(packageName, wrappedActivities); |
| } |
| |
| /** |
| * @return true if the package has any activities that declare that they support |
| * picture-in-picture. |
| */ |
| @VisibleForTesting |
| static boolean checkPackageHasPictureInPictureActivities(String packageName, |
| ActivityInfoWrapper[] activities) { |
| // Skip if it's in the ignored list |
| if (IGNORE_PACKAGE_LIST.contains(packageName)) { |
| return false; |
| } |
| |
| // Iterate through all the activities and check if it is resizeable and supports |
| // picture-in-picture |
| if (activities != null) { |
| for (int i = activities.length - 1; i >= 0; i--) { |
| if (activities[i].supportsPictureInPicture()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public PictureInPictureSettings() { |
| // Do nothing |
| } |
| |
| public PictureInPictureSettings(PackageManagerWrapper pm, UserManagerWrapper um) { |
| mPackageManager = pm; |
| mUserManager = um; |
| } |
| |
| @Override |
| public void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| mContext = getActivity(); |
| mPackageManager = new PackageManagerWrapper(mContext.getPackageManager()); |
| mUserManager = new UserManagerWrapper(mContext.getSystemService(UserManager.class)); |
| mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); |
| setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| // Clear the prefs |
| final PreferenceScreen screen = getPreferenceScreen(); |
| screen.removeAll(); |
| |
| // Fetch the set of applications for each profile which have at least one activity that |
| // declare that they support picture-in-picture |
| final PackageManager pm = mPackageManager.getPackageManager(); |
| final ArrayList<Pair<ApplicationInfo, Integer>> pipApps = |
| collectPipApps(UserHandle.myUserId()); |
| Collections.sort(pipApps, new AppComparator(pm)); |
| |
| // Rebuild the list of prefs |
| final Context prefContext = getPrefContext(); |
| for (final Pair<ApplicationInfo, Integer> appData : pipApps) { |
| final ApplicationInfo appInfo = appData.first; |
| final int userId = appData.second; |
| final UserHandle user = UserHandle.of(userId); |
| final String packageName = appInfo.packageName; |
| final CharSequence label = appInfo.loadLabel(pm); |
| |
| final Preference pref = new Preference(prefContext); |
| pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId)); |
| pref.setTitle(pm.getUserBadgedLabel(label, user)); |
| pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext, |
| appInfo.uid, packageName)); |
| pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { |
| @Override |
| public boolean onPreferenceClick(Preference preference) { |
| AppInfoBase.startAppInfoFragment(PictureInPictureDetails.class, |
| R.string.picture_in_picture_app_detail_title, packageName, appInfo.uid, |
| PictureInPictureSettings.this, -1, getMetricsCategory()); |
| return true; |
| } |
| }); |
| screen.addPreference(pref); |
| } |
| } |
| |
| @Override |
| public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { |
| super.onViewCreated(view, savedInstanceState); |
| setEmptyText(R.string.picture_in_picture_empty_text); |
| } |
| |
| @Override |
| public int getMetricsCategory() { |
| return MetricsEvent.SETTINGS_MANAGE_PICTURE_IN_PICTURE; |
| } |
| |
| /** |
| * @return the list of applications for the given user and all their profiles that have |
| * activities which support PiP. |
| */ |
| ArrayList<Pair<ApplicationInfo, Integer>> collectPipApps(int userId) { |
| final ArrayList<Pair<ApplicationInfo, Integer>> pipApps = new ArrayList<>(); |
| final ArrayList<Integer> userIds = new ArrayList<>(); |
| for (UserInfo user : mUserManager.getProfiles(userId)) { |
| userIds.add(user.id); |
| } |
| |
| for (int id : userIds) { |
| final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser( |
| GET_ACTIVITIES, id); |
| for (PackageInfo packageInfo : installedPackages) { |
| if (checkPackageHasPictureInPictureActivities(packageInfo.packageName, |
| packageInfo.activities)) { |
| pipApps.add(new Pair<>(packageInfo.applicationInfo, id)); |
| } |
| } |
| } |
| return pipApps; |
| } |
| } |