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