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

Commit 93287413 authored by Ankita Vyas's avatar Ankita Vyas Committed by Android (Google) Code Review
Browse files

Merge "Show apps from user and all of its profiles in recents category."

parents bf26483a 4c2ef22a
Loading
Loading
Loading
Loading
+28 −36
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.settings.Utils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -49,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * A helper class that loads recent app data in the background and sends it in a callback to a
@@ -65,10 +65,8 @@ public class RecentAppStatsMixin implements LifecycleObserver, OnStart {
    private final int mMaximumApps;
    private final Context mContext;
    private final PackageManager mPm;
    private final UserManager mUserManager;
    private final PowerManager mPowerManager;
    private final int mWorkUserId;
    private final UsageStatsManager mPersonalUsageStatsManager;
    private final Optional<UsageStatsManager> mWorkUsageStatsManager;
    private final ApplicationsState mApplicationsState;
    private final List<RecentAppStatsListener> mAppStatsListeners;
    private Calendar mCalendar;
@@ -89,13 +87,7 @@ public class RecentAppStatsMixin implements LifecycleObserver, OnStart {
        mMaximumApps = maximumApps;
        mPm = mContext.getPackageManager();
        mPowerManager = mContext.getSystemService(PowerManager.class);
        final UserManager userManager = mContext.getSystemService(UserManager.class);
        mWorkUserId = Utils.getManagedProfileId(userManager, UserHandle.myUserId());
        mPersonalUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
        final UserHandle workUserHandle = Utils.getManagedProfile(userManager);
        mWorkUsageStatsManager = Optional.ofNullable(workUserHandle).map(
                handle -> mContext.createContextAsUser(handle, /* flags */ 0)
                        .getSystemService(UsageStatsManager.class));
        mUserManager = mContext.getSystemService(UserManager.class);
        mApplicationsState = ApplicationsState.getInstance(
                (Application) mContext.getApplicationContext());
        mRecentApps = new ArrayList<>();
@@ -122,34 +114,34 @@ public class RecentAppStatsMixin implements LifecycleObserver, OnStart {
        mCalendar = Calendar.getInstance();
        mCalendar.add(Calendar.DAY_OF_YEAR, -1);

        final int personalUserId = UserHandle.myUserId();
        final List<UsageStats> personalStats =
                getRecentAppsStats(mPersonalUsageStatsManager, personalUserId);
        final List<UsageStats> workStats = mWorkUsageStatsManager
                .map(statsManager -> getRecentAppsStats(statsManager, mWorkUserId))
                .orElse(new ArrayList<>());
        List<UsageStatsWrapper> usageStatsAllUsers = new ArrayList<>();

        // Both lists are already sorted, so we can create a sorted merge in linear time
        int personal = 0;
        int work = 0;
        while (personal < personalStats.size() && work < workStats.size()
                && mRecentApps.size() < limit) {
            UsageStats currentPersonal = personalStats.get(personal);
            UsageStats currentWork = workStats.get(work);
            if (currentPersonal.getLastTimeUsed() > currentWork.getLastTimeUsed()) {
                mRecentApps.add(new UsageStatsWrapper(currentPersonal, personalUserId));
                personal++;
        List<UserHandle> profiles = mUserManager.getUserProfiles();
        for (UserHandle userHandle : profiles) {
            int userId = userHandle.getIdentifier();

            final Optional<UsageStatsManager> usageStatsManager;
            if (userHandle.getIdentifier() == UserHandle.myUserId()) {
                usageStatsManager = Optional.ofNullable(userHandle).map(
                        handle -> mContext.getSystemService(UsageStatsManager.class));
            } else {
                mRecentApps.add(new UsageStatsWrapper(currentWork, mWorkUserId));
                work++;
            }
        }
        while (personal < personalStats.size() && mRecentApps.size() < limit) {
            mRecentApps.add(new UsageStatsWrapper(personalStats.get(personal++), personalUserId));
                usageStatsManager = Optional.ofNullable(userHandle).map(
                        handle -> mContext.createContextAsUser(handle, /* flags */ 0)
                                .getSystemService(UsageStatsManager.class));
            }
        while (work < workStats.size() && mRecentApps.size() < limit) {
            mRecentApps.add(new UsageStatsWrapper(workStats.get(work++), mWorkUserId));

            List<UsageStats> profileStats = usageStatsManager
                    .map(statsManager -> getRecentAppsStats(statsManager, userId))
                    .orElse(new ArrayList<>());
            usageStatsAllUsers.addAll(profileStats.stream()
                        .map(usageStats-> new UsageStatsWrapper(usageStats, userId))
                        .collect(Collectors.toList()));
        }

        // Sort apps by latest timestamp.
        usageStatsAllUsers.sort(
                Comparator.comparingLong(a -> -1 * a.mUsageStats.getLastTimeUsed()));
        mRecentApps.addAll(usageStatsAllUsers.stream().limit(limit).collect(Collectors.toList()));
    }

    private List<UsageStats> getRecentAppsStats(UsageStatsManager usageStatsManager, int userId) {
+93 −12
Original line number Diff line number Diff line
@@ -53,12 +53,16 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

@RunWith(RobolectricTestRunner.class)
public class RecentAppStatsMixinTest {

    private static final UserHandle NORMAL_USER = UserHandle.SYSTEM;
    private static final UserHandle CLONE_USER = new UserHandle(2222);
    private static final UserHandle WORK_USER = new UserHandle(3333);

    @Mock
    private UsageStatsManager mUsageStatsManager;
    @Mock
@@ -75,24 +79,33 @@ public class RecentAppStatsMixinTest {
    private ApplicationInfo mApplicationInfo;
    @Mock
    private PowerManager mPowerManager;
    @Mock
    private UsageStatsManager mCloneUsageStatsManager;
    @Mock
    Context mMockContext;

    private Context mContext;

    private RecentAppStatsMixin mRecentAppStatsMixin;

    @Before
    public void setUp() {
    public void setUp() throws PackageManager.NameNotFoundException {
        MockitoAnnotations.initMocks(this);
        final Context context = spy(RuntimeEnvironment.application);
        when(context.getApplicationContext()).thenReturn(context);
        mContext = spy(RuntimeEnvironment.application);
        when(mContext.getApplicationContext()).thenReturn(mContext);
        ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", mAppState);
        doReturn(mUsageStatsManager).when(context).getSystemService(Context.USAGE_STATS_SERVICE);
        doReturn(mUserManager).when(context).getSystemService(Context.USER_SERVICE);
        doReturn(mPackageManager).when(context).getPackageManager();
        doReturn(mPowerManager).when(context).getSystemService(PowerManager.class);
        doReturn(mUsageStatsManager).when(mContext).getSystemService(UsageStatsManager.class);
        doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
        doReturn(mPackageManager).when(mContext).getPackageManager();
        doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class);
        when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});

        mRecentAppStatsMixin = new RecentAppStatsMixin(context, 3 /* maximumApps */);
        ReflectionHelpers.setField(mRecentAppStatsMixin, "mWorkUsageStatsManager",
                Optional.of(mWorkUsageStatsManager));
        doReturn(mMockContext).when(mContext).createContextAsUser(any(), anyInt());
        doReturn(mMockContext).when(mContext).createPackageContextAsUser(any(), anyInt(), any());
        when(mUserManager.getUserProfiles())
                .thenReturn(new ArrayList<>(Arrays.asList(NORMAL_USER)));

        mRecentAppStatsMixin = new RecentAppStatsMixin(mContext, 3 /* maximumApps */);
    }

    @Test
@@ -336,6 +349,10 @@ public class RecentAppStatsMixinTest {
                .thenReturn(mAppEntry);
        when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
                .thenReturn(new ResolveInfo());
        when(mUserManager.getUserProfiles())
                .thenReturn(new ArrayList<>(Arrays.asList(NORMAL_USER, WORK_USER)));
        when(mMockContext.getSystemService(UsageStatsManager.class))
                .thenReturn(mWorkUsageStatsManager);
        // personal app stats
        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
                .thenReturn(personalStats);
@@ -356,7 +373,8 @@ public class RecentAppStatsMixinTest {
    }

    @Test
    public void loadDisplayableRecentApps_usePersonalAndWorkApps_shouldBeUniquePerProfile() {
    public void loadDisplayableRecentApps_usePersonalAndWorkApps_shouldBeUniquePerProfile()
            throws PackageManager.NameNotFoundException {
        final String firstAppPackageName = "app1.pkg.class";
        final String secondAppPackageName = "app2.pkg.class";
        final List<UsageStats> personalStats = new ArrayList<>();
@@ -383,6 +401,10 @@ public class RecentAppStatsMixinTest {

        when(mAppState.getEntry(anyString(), anyInt()))
                .thenReturn(mAppEntry);
        when(mUserManager.getUserProfiles())
                .thenReturn(new ArrayList<>(Arrays.asList(NORMAL_USER, WORK_USER)));
        when(mMockContext.getSystemService(UsageStatsManager.class))
                .thenReturn(mWorkUsageStatsManager);
        when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
                .thenReturn(new ResolveInfo());
        // personal app stats
@@ -405,4 +427,63 @@ public class RecentAppStatsMixinTest {
        assertThat(mRecentAppStatsMixin.mRecentApps.get(2).mUsageStats.mPackageName).isEqualTo(
                secondAppPackageName);
    }

    @Test
    public void loadDisplayableRecentApps_multipleProfileApps_shouldBeSortedByLastTimeUse()
            throws PackageManager.NameNotFoundException {
        final List<UsageStats> personalStats = new ArrayList<>();
        final UsageStats stats1 = new UsageStats();
        final UsageStats stats2 = new UsageStats();
        stats1.mLastTimeUsed = System.currentTimeMillis();
        stats1.mPackageName = "personal.pkg.class";
        personalStats.add(stats1);

        stats2.mLastTimeUsed = System.currentTimeMillis() - 5000;
        stats2.mPackageName = "personal.pkg.class2";
        personalStats.add(stats2);

        final List<UsageStats> workStats = new ArrayList<>();
        final UsageStats stat3 = new UsageStats();
        stat3.mLastTimeUsed = System.currentTimeMillis() - 2000;
        stat3.mPackageName = "work.pkg.class3";
        workStats.add(stat3);

        final List<UsageStats> cloneStats = new ArrayList<>();
        final UsageStats stat4 = new UsageStats();
        stat4.mLastTimeUsed = System.currentTimeMillis() - 1000;
        stat4.mPackageName = "clone.pkg.class4";
        cloneStats.add(stat4);

        when(mAppState.getEntry(anyString(), anyInt()))
                .thenReturn(mAppEntry);
        when(mUserManager.getUserProfiles())
                .thenReturn(new ArrayList<>(Arrays.asList(NORMAL_USER, CLONE_USER, WORK_USER)));
        when(mMockContext.getSystemService(UsageStatsManager.class))
                .thenReturn(mCloneUsageStatsManager, mWorkUsageStatsManager);
        when(mPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
                .thenReturn(new ResolveInfo());
        // personal app stats
        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
                .thenReturn(personalStats);
        // work app stats
        when(mWorkUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
                .thenReturn(workStats);
        // clone app stats
        when(mCloneUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
                .thenReturn(cloneStats);

        mAppEntry.info = mApplicationInfo;

        mRecentAppStatsMixin.loadDisplayableRecentApps(4);

        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(4);
        assertThat(mRecentAppStatsMixin.mRecentApps.get(0).mUsageStats.mPackageName).isEqualTo(
                "personal.pkg.class");
        assertThat(mRecentAppStatsMixin.mRecentApps.get(1).mUsageStats.mPackageName).isEqualTo(
                "clone.pkg.class4");
        assertThat(mRecentAppStatsMixin.mRecentApps.get(2).mUsageStats.mPackageName).isEqualTo(
                "work.pkg.class3");
        assertThat(mRecentAppStatsMixin.mRecentApps.get(3).mUsageStats.mPackageName).isEqualTo(
                "personal.pkg.class2");
    }
}