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

Commit 4c2ef22a authored by “Ankita's avatar “Ankita
Browse files

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

Previously only personal and work profile apps were displayed.

Bug: 259627902
Test: make RunSettingsRoboTests ROBOTEST_FILTER=RecentAppStatsMixinTest
Change-Id: Id975534343dab3c816a6a18d53ba639a74ff81b0
parent 15066fe3
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");
    }
}