Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d0b1297e authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Fixing issue with PiP settings not showing apps for other profiles."

parents b9a1f1a5 3baa08b4
Loading
Loading
Loading
Loading
+88 −25
Original line number Diff line number Diff line
@@ -22,14 +22,16 @@ import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
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.ArrayMap;
import android.util.IconDrawableFactory;
import android.util.Pair;
import android.view.View;

import com.android.internal.annotations.VisibleForTesting;
@@ -37,9 +39,13 @@ 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 {
@@ -51,8 +57,38 @@ public class PictureInPictureSettings extends EmptyTextSettings {
        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 PackageManager mPackageManager;
    private PackageManagerWrapper mPackageManager;
    private UserManagerWrapper mUserManager;
    private IconDrawableFactory mIconDrawableFactory;

    /**
     * @return true if the package has any activities that declare that they support
@@ -94,12 +130,23 @@ public class PictureInPictureSettings extends EmptyTextSettings {
        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 = mContext.getPackageManager();
        mPackageManager = new PackageManagerWrapper(mContext.getPackageManager());
        mUserManager = new UserManagerWrapper(mContext.getSystemService(UserManager.class));
        mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
        setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
    }

@@ -111,33 +158,25 @@ public class PictureInPictureSettings extends EmptyTextSettings {
        final PreferenceScreen screen = getPreferenceScreen();
        screen.removeAll();

        // Fetch the set of applications which have at least one activity that declare that they
        // support picture-in-picture
        final ArrayMap<String, Boolean> packageToState = new ArrayMap<>();
        final ArrayList<ApplicationInfo> pipApps = new ArrayList<>();
        final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser(
                GET_ACTIVITIES, UserHandle.myUserId());
        for (PackageInfo packageInfo : installedPackages) {
            if (checkPackageHasPictureInPictureActivities(packageInfo.packageName,
                    packageInfo.activities)) {
                final String packageName = packageInfo.applicationInfo.packageName;
                final boolean state = PictureInPictureDetails.getEnterPipStateForPackage(
                        mContext, packageInfo.applicationInfo.uid, packageName);
                pipApps.add(packageInfo.applicationInfo);
                packageToState.put(packageName, state);
            }
        }
        Collections.sort(pipApps, new PackageItemInfo.DisplayNameComparator(mPackageManager));
        // 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 ApplicationInfo appInfo : pipApps) {
        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(mPackageManager);
            final CharSequence label = appInfo.loadLabel(pm);

            final Preference pref = new Preference(prefContext);
            pref.setIcon(appInfo.loadIcon(mPackageManager));
            pref.setTitle(label);
            pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId));
            pref.setTitle(pm.getUserBadgedLabel(label, user));
            pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext,
                    appInfo.uid, packageName));
            pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@@ -163,4 +202,28 @@ public class PictureInPictureSettings extends EmptyTextSettings {
    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;
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -41,4 +41,8 @@ public class UserManagerWrapper {
    public List<UserInfo> getUsers() {
        return mUserManager.getUsers();
    }

    public List<UserInfo> getProfiles(int userHandle) {
        return mUserManager.getProfiles(userHandle);
    }
}
+170 −0
Original line number Diff line number Diff line
/*
 * 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 com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.UserInfo;
import android.util.Pair;

import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.TestConfig;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.wrapper.UserManagerWrapper;
import com.android.settingslib.wrapper.PackageManagerWrapper;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;

import java.util.ArrayList;
import java.util.Collections;

@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class PictureInPictureSettingsTest {

    private static final int PRIMARY_USER_ID = 0;
    private static final int PROFILE_USER_ID = 10;

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    private Context mContext;

    private FakeFeatureFactory mFeatureFactory;
    private PictureInPictureSettings mFragment;
    @Mock
    private PackageManagerWrapper mPackageManager;
    @Mock
    private UserManagerWrapper mUserManager;
    private ArrayList<PackageInfo> mPrimaryUserPackages;
    private ArrayList<PackageInfo> mProfileUserPackages;
    private ArrayList<UserInfo> mUsers;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        FakeFeatureFactory.setupForTest(mContext);
        mFeatureFactory = (FakeFeatureFactory) FakeFeatureFactory.getFactory(mContext);
        mFragment = new PictureInPictureSettings(mPackageManager, mUserManager);
        mPrimaryUserPackages = new ArrayList<>();
        mProfileUserPackages = new ArrayList<>();
        mUsers = new ArrayList<>();
        when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PRIMARY_USER_ID)))
                .thenReturn(mPrimaryUserPackages);
        when(mPackageManager.getInstalledPackagesAsUser(anyInt(), eq(PROFILE_USER_ID)))
                .thenReturn(mProfileUserPackages);
        when(mUserManager.getProfiles(anyInt())).thenReturn(mUsers);
    }

    @Test
    public void testCollectPipApps() {
        PackageInfo primaryP1 = createPackage("Calculator", true);
        PackageInfo primaryP2 = createPackage("Clock", false);
        PackageInfo profileP1 = createPackage("Calculator", false);
        PackageInfo profileP2 = createPackage("Clock", true);

        mPrimaryUserPackages.add(primaryP1);
        mPrimaryUserPackages.add(primaryP2);
        mProfileUserPackages.add(profileP1);
        mProfileUserPackages.add(profileP2);

        ArrayList<Pair<ApplicationInfo, Integer>> apps = mFragment.collectPipApps(PRIMARY_USER_ID);
        assertThat(containsPackages(apps, primaryP1, profileP2));
        assertThat(!containsPackages(apps, primaryP2, profileP1));
    }

    @Test
    public void testAppSort() {
        PackageInfo primaryP1 = createPackage("Android", true);
        PackageInfo primaryP2 = createPackage("Boop", true);
        PackageInfo primaryP3 = createPackage("Deck", true);
        PackageInfo profileP1 = createPackage("Android", true);
        PackageInfo profileP2 = createPackage("Cool", true);
        PackageInfo profileP3 = createPackage("Fast", false);

        mPrimaryUserPackages.add(primaryP1);
        mPrimaryUserPackages.add(primaryP2);
        mPrimaryUserPackages.add(primaryP3);
        mProfileUserPackages.add(profileP1);
        mProfileUserPackages.add(profileP2);
        mProfileUserPackages.add(profileP3);

        ArrayList<Pair<ApplicationInfo, Integer>> apps = mFragment.collectPipApps(PRIMARY_USER_ID);
        Collections.sort(apps, new PictureInPictureSettings.AppComparator(null));
        assertThat(isOrdered(apps, primaryP1, profileP1, primaryP2, profileP2));
    }

    private boolean containsPackages(ArrayList<Pair<ApplicationInfo, Integer>> apps,
            PackageInfo... packages) {
        for (int i = 0; i < packages.length; i++) {
            boolean found = false;
            for (int j = 0; j < apps.size(); j++) {
                if (apps.get(j).first == packages[i].applicationInfo) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }
        return true;
    }

    private boolean isOrdered(ArrayList<Pair<ApplicationInfo, Integer>> apps,
            PackageInfo... packages) {
        if (apps.size() != packages.length) {
            return false;
        }

        for (int i = 0; i < packages.length; i++) {
            if (packages[i].applicationInfo != apps.get(i).first) {
                return false;
            }
        }
        return true;
    }

    private PackageInfo createPackage(String appTitle, boolean supportsPip) {
        PackageInfo pi = new PackageInfo();
        ActivityInfo ai = new ActivityInfo();
        if (supportsPip) {
            ai.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
        }
        pi.activities = new ActivityInfo[1];
        pi.activities[0] = ai;
        pi.applicationInfo = new ApplicationInfo();
        pi.applicationInfo.name = appTitle;
        return pi;
    }
}