Improve launch time for app & notification screen

- We won't query recent apps in getAvailabilityStatus().
And then we do that in background thread.
So, we can launch app & notificatins screen as soon as possible.

- Create a RecentAppStatsMixin class which is responsible
for maintaining the recent apps data.

- Controllers and fragment update their UI after they got
onReloadDataCompleted callback from RecentAppStatsMixin.

Test: manual, robotest
Fixes: 128849426
Fixes: 126453868
Change-Id: I636d5878cb5d53496978fe613c625382d8d382bc
diff --git a/src/com/android/settings/applications/AllAppsInfoPreferenceController.java b/src/com/android/settings/applications/AllAppsInfoPreferenceController.java
index d39c6e9..325b25a 100644
--- a/src/com/android/settings/applications/AllAppsInfoPreferenceController.java
+++ b/src/com/android/settings/applications/AllAppsInfoPreferenceController.java
@@ -19,39 +19,54 @@
 import android.app.usage.UsageStats;
 import android.content.Context;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 
 import java.util.List;
 
-public class AllAppsInfoPreferenceController extends BasePreferenceController {
+public class AllAppsInfoPreferenceController extends BasePreferenceController
+        implements RecentAppStatsMixin.RecentAppStatsListener {
 
-    private List<UsageStats> mRecentApps;
+    @VisibleForTesting
+    Preference mPreference;
 
     public AllAppsInfoPreferenceController(Context context, String key) {
         super(context, key);
     }
 
-    public void setRecentApps(List<UsageStats> recentApps) {
-        mRecentApps = recentApps;
-    }
-
     @Override
     public int getAvailabilityStatus() {
-        return mRecentApps == null || mRecentApps.isEmpty() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+        return AVAILABLE;
     }
 
     @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        mPreference = screen.findPreference(getPreferenceKey());
+        // In most cases, device has recently opened apps. So, we hide it by default.
+        mPreference.setVisible(false);
+    }
+
+    @Override
+    public void onReloadDataCompleted(@NonNull List<UsageStats> recentApps) {
+        // If device has recently opened apps, we don't show all apps preference.
+        if (!recentApps.isEmpty()) {
+            mPreference.setVisible(false);
+            return;
+        }
+
+        mPreference.setVisible(true);
         // Show total number of installed apps as See all's summary.
         new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
                 mContext.getPackageManager()) {
             @Override
             protected void onCountComplete(int num) {
-                preference.setSummary(mContext.getString(R.string.apps_summary, num));
+                mPreference.setSummary(mContext.getString(R.string.apps_summary, num));
             }
         }.execute();
     }
diff --git a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
index c32a33d..5e57908 100644
--- a/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
+++ b/src/com/android/settings/applications/AppAndNotificationDashboardFragment.java
@@ -17,8 +17,13 @@
 package com.android.settings.applications;
 
 import android.app.settings.SettingsEnums;
+import android.app.usage.UsageStats;
 import android.content.Context;
+import android.os.Bundle;
 import android.provider.SearchIndexableResource;
+import android.view.View;
+
+import androidx.annotation.NonNull;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
@@ -27,17 +32,21 @@
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.AppEntitiesHeaderController;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
 @SearchIndexable
-public class AppAndNotificationDashboardFragment extends DashboardFragment {
+public class AppAndNotificationDashboardFragment extends DashboardFragment
+        implements RecentAppStatsMixin.RecentAppStatsListener {
 
     private static final String TAG = "AppAndNotifDashboard";
 
-    private boolean mIsFirstLaunch;
+    private View mProgressHeader;
+    private View mProgressAnimation;
+    private RecentAppStatsMixin mRecentAppStatsMixin;
     private RecentAppsPreferenceController mRecentAppsPreferenceController;
     private AllAppsInfoPreferenceController mAllAppsInfoPreferenceController;
 
@@ -66,29 +75,40 @@
         super.onAttach(context);
 
         use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
+
+        mRecentAppStatsMixin = new RecentAppStatsMixin(context,
+                AppEntitiesHeaderController.MAXIMUM_APPS);
+        getSettingsLifecycle().addObserver(mRecentAppStatsMixin);
+        mRecentAppStatsMixin.addListener(this);
+
         mRecentAppsPreferenceController = use(RecentAppsPreferenceController.class);
         mRecentAppsPreferenceController.setFragment(this /* fragment */);
+        mRecentAppStatsMixin.addListener(mRecentAppsPreferenceController);
 
         mAllAppsInfoPreferenceController = use(AllAppsInfoPreferenceController.class);
-        mAllAppsInfoPreferenceController.setRecentApps(
-                mRecentAppsPreferenceController.getRecentApps());
-
-        mIsFirstLaunch = true;
+        mRecentAppStatsMixin.addListener(mAllAppsInfoPreferenceController);
     }
 
     @Override
-    public void onResume() {
-        if (!mIsFirstLaunch) {
-            mRecentAppsPreferenceController.reloadData();
-            mAllAppsInfoPreferenceController.setRecentApps(
-                    mRecentAppsPreferenceController.getRecentApps());
-        }
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        mProgressHeader = setPinnedHeaderView(R.layout.progress_header);
+        mProgressAnimation = mProgressHeader.findViewById(R.id.progress_bar_animation);
+        setLoadingEnabled(false);
+    }
 
-        super.onResume();
-        mIsFirstLaunch = false;
+    @Override
+    public void onStart() {
+        super.onStart();
+        setLoadingEnabled(true);
+    }
 
-        if (mRecentAppsPreferenceController.isAvailable()) {
-            Utils.setActionBarShadowAnimation(getActivity(), getSettingsLifecycle(), getListView());
+    @Override
+    public void onReloadDataCompleted(@NonNull List<UsageStats> recentApps) {
+        setLoadingEnabled(false);
+        if (!recentApps.isEmpty()) {
+            Utils.setActionBarShadowAnimation(getActivity(), getSettingsLifecycle(),
+                    getListView());
         }
     }
 
@@ -97,6 +117,13 @@
         return buildPreferenceControllers(context);
     }
 
+    private void setLoadingEnabled(boolean enabled) {
+        if (mProgressHeader != null && mProgressAnimation != null) {
+            mProgressHeader.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+            mProgressAnimation.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         controllers.add(new EmergencyBroadcastPreferenceController(context,
diff --git a/src/com/android/settings/applications/RecentAppStatsMixin.java b/src/com/android/settings/applications/RecentAppStatsMixin.java
new file mode 100644
index 0000000..4bf3864
--- /dev/null
+++ b/src/com/android/settings/applications/RecentAppStatsMixin.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2019 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.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+
+import android.app.Application;
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+
+public class RecentAppStatsMixin implements Comparator<UsageStats>, LifecycleObserver, OnStart {
+
+    private static final String TAG = "RecentAppStatsMixin";
+    private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
+
+    @VisibleForTesting
+    final List<UsageStats> mRecentApps;
+    private final int mUserId;
+    private final int mMaximumApps;
+    private final Context mContext;
+    private final PackageManager mPm;
+    private final PowerManager mPowerManager;;
+    private final UsageStatsManager mUsageStatsManager;
+    private final ApplicationsState mApplicationsState;
+    private final List<RecentAppStatsListener> mAppStatsListeners;
+    private Calendar mCalendar;
+
+    static {
+        SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList(
+                "android",
+                "com.android.phone",
+                SETTINGS_PACKAGE_NAME,
+                "com.android.systemui",
+                "com.android.providers.calendar",
+                "com.android.providers.media"
+        ));
+    }
+
+    public RecentAppStatsMixin(Context context, int maximumApps) {
+        mContext = context;
+        mMaximumApps = maximumApps;
+        mUserId = UserHandle.myUserId();
+        mPm = mContext.getPackageManager();
+        mPowerManager = mContext.getSystemService(PowerManager.class);
+        mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
+        mApplicationsState = ApplicationsState.getInstance(
+                (Application) mContext.getApplicationContext());
+        mRecentApps = new ArrayList<>();
+        mAppStatsListeners = new ArrayList<>();
+    }
+
+    @Override
+    public void onStart() {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            loadDisplayableRecentApps(mMaximumApps);
+            for (RecentAppStatsListener listener : mAppStatsListeners) {
+                ThreadUtils.postOnMainThread(() -> listener.onReloadDataCompleted(mRecentApps));
+            }
+        });
+    }
+
+    @Override
+    public final int compare(UsageStats a, UsageStats b) {
+        // return by descending order
+        return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
+    }
+
+    public void addListener(@NonNull RecentAppStatsListener listener) {
+        mAppStatsListeners.add(listener);
+    }
+
+    @VisibleForTesting
+    void loadDisplayableRecentApps(int number) {
+        mRecentApps.clear();
+        mCalendar = Calendar.getInstance();
+        mCalendar.add(Calendar.DAY_OF_YEAR, -1);
+        final List<UsageStats> mStats = mPowerManager.isPowerSaveMode()
+                ? new ArrayList<>()
+                : mUsageStatsManager.queryUsageStats(
+                        UsageStatsManager.INTERVAL_BEST, mCalendar.getTimeInMillis(),
+                        System.currentTimeMillis());
+
+        final Map<String, UsageStats> map = new ArrayMap<>();
+        final int statCount = mStats.size();
+        for (int i = 0; i < statCount; i++) {
+            final UsageStats pkgStats = mStats.get(i);
+            if (!shouldIncludePkgInRecents(pkgStats)) {
+                continue;
+            }
+            final String pkgName = pkgStats.getPackageName();
+            final UsageStats existingStats = map.get(pkgName);
+            if (existingStats == null) {
+                map.put(pkgName, pkgStats);
+            } else {
+                existingStats.add(pkgStats);
+            }
+        }
+        final List<UsageStats> packageStats = new ArrayList<>();
+        packageStats.addAll(map.values());
+        Collections.sort(packageStats, this /* comparator */);
+        int count = 0;
+        for (UsageStats stat : packageStats) {
+            final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(
+                    stat.getPackageName(), mUserId);
+            if (appEntry == null) {
+                continue;
+            }
+            mRecentApps.add(stat);
+            count++;
+            if (count >= number) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Whether or not the app should be included in recent list.
+     */
+    private boolean shouldIncludePkgInRecents(UsageStats stat) {
+        final String pkgName = stat.getPackageName();
+        if (stat.getLastTimeUsed() < mCalendar.getTimeInMillis()) {
+            Log.d(TAG, "Invalid timestamp (usage time is more than 24 hours ago), skipping "
+                    + pkgName);
+            return false;
+        }
+
+        if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) {
+            Log.d(TAG, "System package, skipping " + pkgName);
+            return false;
+        }
+        if (AppUtils.isHiddenSystemModule(mContext, pkgName)) {
+            return false;
+        }
+        final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
+                .setPackage(pkgName);
+
+        if (mPm.resolveActivity(launchIntent, 0) == null) {
+            // Not visible on launcher -> likely not a user visible app, skip if non-instant.
+            final ApplicationsState.AppEntry appEntry =
+                    mApplicationsState.getEntry(pkgName, mUserId);
+            if (appEntry == null || appEntry.info == null || !AppUtils.isInstant(appEntry.info)) {
+                Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public interface RecentAppStatsListener {
+
+        void onReloadDataCompleted(List<UsageStats> recentApps);
+    }
+}
diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java
index be86dd5..4f5ec01 100644
--- a/src/com/android/settings/applications/RecentAppsPreferenceController.java
+++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java
@@ -16,24 +16,17 @@
 
 package com.android.settings.applications;
 
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-
 import android.app.Application;
 import android.app.settings.SettingsEnums;
 import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
 import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.icu.text.RelativeDateTimeFormatter;
-import android.os.PowerManager;
 import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
 import androidx.preference.Preference;
@@ -44,35 +37,24 @@
 import com.android.settings.applications.manageapplications.ManageApplications;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.core.SubSettingLauncher;
-import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
 import com.android.settingslib.utils.StringUtil;
 import com.android.settingslib.widget.AppEntitiesHeaderController;
 import com.android.settingslib.widget.AppEntityInfo;
 import com.android.settingslib.widget.LayoutPreference;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * This controller displays up to three recently used apps.
  * If there is no recently used app, we only show up an "App Info" preference.
  */
 public class RecentAppsPreferenceController extends BasePreferenceController
-        implements Comparator<UsageStats> {
+        implements RecentAppStatsMixin.RecentAppStatsListener {
 
     @VisibleForTesting
     static final String KEY_DIVIDER = "recent_apps_divider";
 
-    private static final String TAG = "RecentAppsCtrl";
-    private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();
-
     @VisibleForTesting
     AppEntitiesHeaderController mAppEntitiesController;
     @VisibleForTesting
@@ -80,41 +62,19 @@
     @VisibleForTesting
     Preference mDivider;
 
-    private final PackageManager mPm;
-    private final UsageStatsManager mUsageStatsManager;
     private final ApplicationsState mApplicationsState;
     private final int mUserId;
     private final IconDrawableFactory mIconDrawableFactory;
-    private final PowerManager mPowerManager;
 
     private Fragment mHost;
-    private Calendar mCal;
-    private List<UsageStats> mStats;
     private List<UsageStats> mRecentApps;
-    private boolean mHasRecentApps;
-
-    static {
-        SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList(
-                "android",
-                "com.android.phone",
-                SETTINGS_PACKAGE_NAME,
-                "com.android.systemui",
-                "com.android.providers.calendar",
-                "com.android.providers.media"
-        ));
-    }
 
     public RecentAppsPreferenceController(Context context, String key) {
         super(context, key);
         mApplicationsState = ApplicationsState.getInstance(
                 (Application) mContext.getApplicationContext());
         mUserId = UserHandle.myUserId();
-        mPm = mContext.getPackageManager();
         mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
-        mPowerManager = mContext.getSystemService(PowerManager.class);
-        mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
-        mRecentApps = new ArrayList<>();
-        reloadData();
     }
 
     public void setFragment(Fragment fragment) {
@@ -123,7 +83,7 @@
 
     @Override
     public int getAvailabilityStatus() {
-        return mRecentApps.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
+        return AVAILABLE_UNSEARCHABLE;
     }
 
     @Override
@@ -131,7 +91,7 @@
         super.displayPreference(screen);
 
         mDivider = screen.findPreference(KEY_DIVIDER);
-        mRecentAppsPreference = (LayoutPreference) screen.findPreference(getPreferenceKey());
+        mRecentAppsPreference = screen.findPreference(getPreferenceKey());
         final View view = mRecentAppsPreference.findViewById(R.id.app_entities_header);
         mAppEntitiesController = AppEntitiesHeaderController.newInstance(mContext, view)
                 .setHeaderTitleRes(R.string.recent_app_category_title)
@@ -143,14 +103,11 @@
                             .setSourceMetricsCategory(SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY)
                             .launch();
                 });
-
-        refreshUi();
     }
 
     @Override
-    public void updateState(Preference preference) {
-        super.updateState(preference);
-
+    public void onReloadDataCompleted(@NonNull List<UsageStats> recentApps) {
+        mRecentApps = recentApps;
         refreshUi();
         // Show total number of installed apps as See all's summary.
         new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
@@ -164,38 +121,17 @@
         }.execute();
     }
 
-    @Override
-    public final int compare(UsageStats a, UsageStats b) {
-        // return by descending order
-        return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
-    }
-
-    List<UsageStats> getRecentApps() {
-        return mRecentApps;
-    }
-
-    @VisibleForTesting
-    void refreshUi() {
-        if (mRecentApps != null && !mRecentApps.isEmpty()) {
+    private void refreshUi() {
+        if (!mRecentApps.isEmpty()) {
             displayRecentApps();
+            mRecentAppsPreference.setVisible(true);
+            mDivider.setVisible(true);
         } else {
             mDivider.setVisible(false);
+            mRecentAppsPreference.setVisible(false);
         }
     }
 
-    @VisibleForTesting
-    void reloadData() {
-        mCal = Calendar.getInstance();
-        mCal.add(Calendar.DAY_OF_YEAR, -1);
-        mStats = mPowerManager.isPowerSaveMode()
-                ? new ArrayList<>()
-                : mUsageStatsManager.queryUsageStats(
-                        UsageStatsManager.INTERVAL_BEST, mCal.getTimeInMillis(),
-                        System.currentTimeMillis());
-
-        updateDisplayableRecentAppList();
-    }
-
     private void displayRecentApps() {
         int showAppsCount = 0;
 
@@ -209,8 +145,6 @@
                 break;
             }
         }
-        mAppEntitiesController.apply();
-        mDivider.setVisible(true);
     }
 
     private AppEntityInfo createAppEntity(UsageStats stat) {
@@ -234,73 +168,4 @@
                                 SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY))
                 .build();
     }
-
-    private void updateDisplayableRecentAppList() {
-        mRecentApps.clear();
-        final Map<String, UsageStats> map = new ArrayMap<>();
-        final int statCount = mStats.size();
-        for (int i = 0; i < statCount; i++) {
-            final UsageStats pkgStats = mStats.get(i);
-            if (!shouldIncludePkgInRecents(pkgStats)) {
-                continue;
-            }
-            final String pkgName = pkgStats.getPackageName();
-            final UsageStats existingStats = map.get(pkgName);
-            if (existingStats == null) {
-                map.put(pkgName, pkgStats);
-            } else {
-                existingStats.add(pkgStats);
-            }
-        }
-        final List<UsageStats> packageStats = new ArrayList<>();
-        packageStats.addAll(map.values());
-        Collections.sort(packageStats, this /* comparator */);
-        int count = 0;
-        for (UsageStats stat : packageStats) {
-            final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(
-                    stat.getPackageName(), mUserId);
-            if (appEntry == null) {
-                continue;
-            }
-            mRecentApps.add(stat);
-            count++;
-            if (count >= AppEntitiesHeaderController.MAXIMUM_APPS) {
-                break;
-            }
-        }
-    }
-
-
-    /**
-     * Whether or not the app should be included in recent list.
-     */
-    private boolean shouldIncludePkgInRecents(UsageStats stat) {
-        final String pkgName = stat.getPackageName();
-        if (stat.getLastTimeUsed() < mCal.getTimeInMillis()) {
-            Log.d(TAG, "Invalid timestamp (usage time is more than 24 hours ago), skipping "
-                    + pkgName);
-            return false;
-        }
-
-        if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) {
-            Log.d(TAG, "System package, skipping " + pkgName);
-            return false;
-        }
-        if (AppUtils.isHiddenSystemModule(mContext, pkgName)) {
-            return false;
-        }
-        final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
-                .setPackage(pkgName);
-
-        if (mPm.resolveActivity(launchIntent, 0) == null) {
-            // Not visible on launcher -> likely not a user visible app, skip if non-instant.
-            final ApplicationsState.AppEntry appEntry =
-                    mApplicationsState.getEntry(pkgName, mUserId);
-            if (appEntry == null || appEntry.info == null || !AppUtils.isInstant(appEntry.info)) {
-                Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName);
-                return false;
-            }
-        }
-        return true;
-    }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java
index ec3cf65..2944db2 100644
--- a/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/AllAppsInfoPreferenceControllerTest.java
@@ -17,16 +17,25 @@
 package com.android.settings.applications;
 
 import static com.android.settings.core.BasePreferenceController.AVAILABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
 import android.app.usage.UsageStats;
 import android.content.Context;
+import android.os.UserManager;
+
+import androidx.preference.Preference;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
@@ -36,29 +45,45 @@
 @RunWith(RobolectricTestRunner.class)
 public class AllAppsInfoPreferenceControllerTest {
 
+    @Mock
+    private UserManager mUserManager;
     private AllAppsInfoPreferenceController mController;
 
     @Before
     public void setUp() {
-        final Context context = RuntimeEnvironment.application;
+        MockitoAnnotations.initMocks(this);
+        final Context context = spy(RuntimeEnvironment.application);
+        final Preference preference = new Preference(context);
+        doReturn(mUserManager).when(context).getSystemService(Context.USER_SERVICE);
+        when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
         mController = new AllAppsInfoPreferenceController(context, "test_key");
+        mController.mPreference = preference;
     }
 
     @Test
-    public void getAvailabilityStatus_hasRecentApps_shouldReturnConditionallyUnavailable() {
+    public void getAvailabilityStatus_shouldReturnAVAILABLE() {
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    }
+
+    @Test
+    public void onReloadDataCompleted_recentAppsSet_hidePreference() {
         final List<UsageStats> stats = new ArrayList<>();
         final UsageStats stat1 = new UsageStats();
         stat1.mLastTimeUsed = System.currentTimeMillis();
         stat1.mPackageName = "pkg.class";
         stats.add(stat1);
-        mController.setRecentApps(stats);
 
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+        mController.onReloadDataCompleted(stats);
+
+        assertThat(mController.mPreference.isVisible()).isFalse();
     }
 
     @Test
-    public void getAvailabilityStatus_noRecentApps_shouldReturnAvailable() {
-        // No data
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+    public void onReloadDataCompleted_noRecentAppSet_showPreference() {
+        final List<UsageStats> stats = new ArrayList<>();
+
+        mController.onReloadDataCompleted(stats);
+
+        assertThat(mController.mPreference.isVisible()).isTrue();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java
new file mode 100644
index 0000000..0fb2a7e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/RecentAppStatsMixinTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2019 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.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ModuleInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class RecentAppStatsMixinTest {
+
+    @Mock
+    private UsageStatsManager mUsageStatsManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private ApplicationsState mAppState;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ApplicationsState.AppEntry mAppEntry;
+    @Mock
+    private ApplicationInfo mApplicationInfo;
+    @Mock
+    private PowerManager mPowerManager;
+
+    private RecentAppStatsMixin mRecentAppStatsMixin;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        final Context context = spy(RuntimeEnvironment.application);
+        when(context.getApplicationContext()).thenReturn(context);
+        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);
+        when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
+
+        mRecentAppStatsMixin = new RecentAppStatsMixin(context, 3 /* maximumApps */);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_oneValidRecentAppSet_shouldHaveOneRecentApp() {
+        final List<UsageStats> stats = new ArrayList<>();
+        final UsageStats stat1 = new UsageStats();
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "pkg.class";
+        stats.add(stat1);
+        // stat1 is valid app.
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+                .thenReturn(new ResolveInfo());
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+        mAppEntry.info = mApplicationInfo;
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_threeValidRecentAppsSet_shouldHaveThreeRecentApps() {
+        final List<UsageStats> stats = new ArrayList<>();
+        final UsageStats stat1 = new UsageStats();
+        final UsageStats stat2 = new UsageStats();
+        final UsageStats stat3 = new UsageStats();
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "pkg.class";
+        stats.add(stat1);
+
+        stat2.mLastTimeUsed = System.currentTimeMillis();
+        stat2.mPackageName = "pkg.class2";
+        stats.add(stat2);
+
+        stat3.mLastTimeUsed = System.currentTimeMillis();
+        stat3.mPackageName = "pkg.class3";
+        stats.add(stat3);
+
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+                .thenReturn(new ResolveInfo());
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+        mAppEntry.info = mApplicationInfo;
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(3);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_oneValidAndTwoInvalidSet_shouldHaveOneRecentApp() {
+        final List<UsageStats> stats = new ArrayList<>();
+        final UsageStats stat1 = new UsageStats();
+        final UsageStats stat2 = new UsageStats();
+        final UsageStats stat3 = new UsageStats();
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "pkg.class";
+        stats.add(stat1);
+
+        stat2.mLastTimeUsed = System.currentTimeMillis();
+        stat2.mPackageName = "com.android.settings";
+        stats.add(stat2);
+
+        stat3.mLastTimeUsed = System.currentTimeMillis();
+        stat3.mPackageName = "pkg.class3";
+        stats.add(stat3);
+
+        // stat1, stat2 are valid apps. stat3 is invalid.
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
+                .thenReturn(null);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+                .thenReturn(new ResolveInfo());
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+        mAppEntry.info = mApplicationInfo;
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        // Only stat1. stat2 is skipped because of the package name, stat3 skipped because
+        // it's invalid app.
+        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_oneInstantAppSet_shouldHaveOneRecentApp() {
+        final List<UsageStats> stats = new ArrayList<>();
+        // Instant app.
+        final UsageStats stat = new UsageStats();
+        stat.mLastTimeUsed = System.currentTimeMillis() + 200;
+        stat.mPackageName = "com.foo.barinstant";
+        stats.add(stat);
+
+        ApplicationsState.AppEntry statEntry = mock(ApplicationsState.AppEntry.class);
+        statEntry.info = mApplicationInfo;
+
+        when(mAppState.getEntry(stat.mPackageName, UserHandle.myUserId())).thenReturn(statEntry);
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+
+        // Make sure stat is considered an instant app.
+        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
+                (InstantAppDataProvider) (ApplicationInfo info) -> info == statEntry.info);
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_withNullAppEntryOrInfo_shouldNotCrash() {
+        final List<UsageStats> stats = new ArrayList<>();
+        final UsageStats stat1 = new UsageStats();
+        final UsageStats stat2 = new UsageStats();
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "pkg.class";
+        stats.add(stat1);
+
+        stat2.mLastTimeUsed = System.currentTimeMillis();
+        stat2.mPackageName = "pkg.class2";
+        stats.add(stat2);
+
+        // app1 has AppEntry with null info, app2 has null AppEntry.
+        mAppEntry.info = null;
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
+                .thenReturn(null);
+
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+
+        // We should not crash here.
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_hiddenSystemModuleSet_shouldNotHaveHiddenSystemModule() {
+        final List<UsageStats> stats = new ArrayList<>();
+        // Regular app.
+        final UsageStats stat1 = new UsageStats();
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "com.foo.bar";
+        stats.add(stat1);
+
+        // Hidden system module.
+        final UsageStats stat2 = new UsageStats();
+        stat2.mLastTimeUsed = System.currentTimeMillis() + 200;
+        stat2.mPackageName = "com.foo.hidden";
+        stats.add(stat2);
+
+        ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class);
+        ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class);
+        stat1Entry.info = mApplicationInfo;
+        stat2Entry.info = mApplicationInfo;
+
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry);
+        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry);
+
+        final ModuleInfo moduleInfo1 = new ModuleInfo();
+        moduleInfo1.setPackageName(stat1.mPackageName);
+        moduleInfo1.setHidden(false);
+
+        final ModuleInfo moduleInfo2 = new ModuleInfo();
+        moduleInfo2.setPackageName(stat2.mPackageName);
+        moduleInfo2.setHidden(true);
+
+        ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", null);
+        final List<ModuleInfo> modules = new ArrayList<>();
+        modules.add(moduleInfo2);
+        when(mPackageManager.getInstalledModules(anyInt() /* flags */))
+                .thenReturn(modules);
+
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+                .thenReturn(new ResolveInfo());
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        assertThat(mRecentAppStatsMixin.mRecentApps.size()).isEqualTo(1);
+    }
+
+    @Test
+    public void loadDisplayableRecentApps_powerSaverModeOn_shouldHaveEmptyList() {
+        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
+
+        final List<UsageStats> stats = new ArrayList<>();
+        final UsageStats stat1 = new UsageStats();
+
+        stat1.mLastTimeUsed = System.currentTimeMillis();
+        stat1.mPackageName = "pkg.class";
+        stats.add(stat1);
+
+        // stat1, stat2 are valid apps. stat3 is invalid.
+        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
+                .thenReturn(mAppEntry);
+        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
+                .thenReturn(new ResolveInfo());
+        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
+                .thenReturn(stats);
+        mAppEntry.info = mApplicationInfo;
+
+        mRecentAppStatsMixin.loadDisplayableRecentApps(3);
+
+        assertThat(mRecentAppStatsMixin.mRecentApps).isEmpty();
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
index 1a28f37..2928d6f 100644
--- a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java
@@ -16,33 +16,23 @@
 
 package com.android.settings.applications;
 
-import static com.android.settings.core.BasePreferenceController.AVAILABLE;
 import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
-import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.ModuleInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.PowerManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.view.LayoutInflater;
@@ -53,9 +43,7 @@
 import androidx.preference.PreferenceScreen;
 
 import com.android.settings.R;
-import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
 import com.android.settingslib.widget.AppEntitiesHeaderController;
 import com.android.settingslib.widget.AppEntityInfo;
 import com.android.settingslib.widget.LayoutPreference;
@@ -63,7 +51,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -79,8 +66,6 @@
     @Mock
     private PreferenceScreen mScreen;
     @Mock
-    private UsageStatsManager mUsageStatsManager;
-    @Mock
     private UserManager mUserManager;
     @Mock
     private ApplicationsState mAppState;
@@ -91,11 +76,8 @@
     @Mock
     private ApplicationInfo mApplicationInfo;
     @Mock
-    private PowerManager mPowerManager;
-    @Mock
     private Fragment mFragment;
 
-    private LayoutPreference mRecentAppsPreference;
     private RecentAppsPreferenceController mController;
 
     @Before
@@ -104,92 +86,33 @@
         final Context context = spy(RuntimeEnvironment.application);
         when(context.getApplicationContext()).thenReturn(context);
         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);
         when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
 
         final View appEntitiesHeaderView = LayoutInflater.from(context).inflate(
                 R.layout.app_entities_header, null /* root */);
         final Preference dividerPreference = new Preference(context);
-        mRecentAppsPreference = spy(new LayoutPreference(context, appEntitiesHeaderView));
+        final LayoutPreference recentAppsPreference =
+                spy(new LayoutPreference(context, appEntitiesHeaderView));
 
         mController = spy(new RecentAppsPreferenceController(context, "test_key"));
         mController.setFragment(mFragment);
+
         mController.mAppEntitiesController = mock(AppEntitiesHeaderController.class);
-        mController.mRecentAppsPreference = mRecentAppsPreference;
+        mController.mRecentAppsPreference = recentAppsPreference;
         mController.mDivider = dividerPreference;
 
         when(mScreen.findPreference(RecentAppsPreferenceController.KEY_DIVIDER))
                 .thenReturn(dividerPreference);
-        when(mScreen.findPreference("test_key")).thenReturn(mRecentAppsPreference);
-        when(mRecentAppsPreference.findViewById(R.id.app_entities_header)).thenReturn(
+        when(mScreen.findPreference("test_key")).thenReturn(recentAppsPreference);
+        when(recentAppsPreference.findViewById(R.id.app_entities_header)).thenReturn(
                 appEntitiesHeaderView);
     }
 
     @Test
-    public void getAvailabilityStatus_hasRecentApps_shouldReturnAvailable() {
-        final List<UsageStats> stats = new ArrayList<>();
-        final UsageStats stat1 = new UsageStats();
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "pkg.class";
-        stats.add(stat1);
-        // stat1 is valid app.
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
-                .thenReturn(mAppEntry);
-        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-        mAppEntry.info = mApplicationInfo;
-        mController.reloadData();
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_noRecentApps_shouldReturnConditionallyUnavailable() {
-        // No data
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Test
-    public void getAvailabilityStatus_powerSaverModeOn_shouldReturnConditionallyUnavailable() {
-        when(mPowerManager.isPowerSaveMode()).thenReturn(true);
-
-        final List<UsageStats> stats = new ArrayList<>();
-        final UsageStats stat1 = new UsageStats();
-
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "pkg.class";
-        stats.add(stat1);
-
-        // stat1, stat2 are valid apps. stat3 is invalid.
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
-                .thenReturn(mAppEntry);
-        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-        mAppEntry.info = mApplicationInfo;
-        mController.reloadData();
-
-        assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
-    }
-
-    @Test
-    public void displayPreference_shouldNotReloadData() {
-        mController.displayPreference(mScreen);
-
-        verify(mController, never()).reloadData();
-    }
-
-    @Test
-    public void displayPreference_shouldRefreshUi() {
-        mController.displayPreference(mScreen);
-
-        verify(mController).refreshUi();
+    public void getAvailabilityStatus_shouldReturnAVAILABLE_UNSEARCHABLE() {
+        assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
     }
 
     @Test
@@ -200,7 +123,7 @@
     }
 
     @Test
-    public void updateState_threeValidRecentOpenAppsSet_setAppEntityThreeTime() {
+    public void onReloadDataCompleted_threeValidRecentOpenAppsSet_setAppEntityThreeTime() {
         final List<UsageStats> stats = new ArrayList<>();
         final UsageStats stat1 = new UsageStats();
         final UsageStats stat2 = new UsageStats();
@@ -216,22 +139,15 @@
         stat3.mLastTimeUsed = System.currentTimeMillis();
         stat3.mPackageName = "pkg.class3";
         stats.add(stat3);
-
-        // stat1, stat2 are valid apps. stat3 is invalid.
         when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
                 .thenReturn(mAppEntry);
         when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
                 .thenReturn(mAppEntry);
         when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
                 .thenReturn(mAppEntry);
-        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
         mAppEntry.info = mApplicationInfo;
-        mController.reloadData();
 
-        mController.updateState(mRecentAppsPreference);
+        mController.onReloadDataCompleted(stats);
 
         verify(mController.mAppEntitiesController, times(3))
                 .setAppEntity(anyInt(), any(AppEntityInfo.class));
@@ -240,165 +156,12 @@
     }
 
     @Test
-    public void updateState_oneValidRecentOpenAppSet_setAppEntityOneTime() {
+    public void onReloadDataCompleted_noRecentOpenAppsSet_shouldHideRecentAppPreference() {
         final List<UsageStats> stats = new ArrayList<>();
-        final UsageStats stat1 = new UsageStats();
-        final UsageStats stat2 = new UsageStats();
-        final UsageStats stat3 = new UsageStats();
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "pkg.class";
-        stats.add(stat1);
 
-        stat2.mLastTimeUsed = System.currentTimeMillis();
-        stat2.mPackageName = "com.android.settings";
-        stats.add(stat2);
+        mController.onReloadDataCompleted(stats);
 
-        stat3.mLastTimeUsed = System.currentTimeMillis();
-        stat3.mPackageName = "pkg.class3";
-        stats.add(stat3);
-
-        // stat1, stat2 are valid apps. stat3 is invalid.
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
-                .thenReturn(mAppEntry);
-        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
-                .thenReturn(mAppEntry);
-        when(mAppState.getEntry(stat3.mPackageName, UserHandle.myUserId()))
-                .thenReturn(null);
-        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-        mAppEntry.info = mApplicationInfo;
-        mController.reloadData();
-
-        mController.updateState(mRecentAppsPreference);
-
-        // Only add stat1. stat2 is skipped because of the package name, stat3 skipped because
-        // it's invalid app.
-        verify(mController.mAppEntitiesController, times(1))
-                .setAppEntity(anyInt(), any(AppEntityInfo.class));
-        assertThat(mController.mRecentAppsPreference.isVisible()).isTrue();
-        assertThat(mController.mDivider.isVisible()).isTrue();
-    }
-
-    @Test
-    public void updateState_instantAppSet_shouldSetAppEntityForInstantApp() {
-        // Regular app.
-        final List<UsageStats> stats = new ArrayList<>();
-        final UsageStats stat1 = new UsageStats();
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "com.foo.bar";
-        stats.add(stat1);
-
-        // Instant app.
-        final UsageStats stat2 = new UsageStats();
-        stat2.mLastTimeUsed = System.currentTimeMillis() + 200;
-        stat2.mPackageName = "com.foo.barinstant";
-        stats.add(stat2);
-
-        ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class);
-        ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class);
-        stat1Entry.info = mApplicationInfo;
-        stat2Entry.info = mApplicationInfo;
-
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry);
-        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry);
-
-        // Only the regular app stat1 should have its intent resolve.
-        when(mPackageManager.resolveActivity(argThat(intentMatcher(stat1.mPackageName)), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-
-        // Make sure stat2 is considered an instant app.
-        ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
-                (InstantAppDataProvider) (ApplicationInfo info) -> info == stat2Entry.info);
-        mController.reloadData();
-
-        mController.updateState(mRecentAppsPreference);
-
-        verify(mController.mAppEntitiesController, times(2))
-                .setAppEntity(anyInt(), any(AppEntityInfo.class));
-    }
-
-    @Test
-    public void updateState_withNullAppEntryOrInfo_shouldNotCrash() {
-        final List<UsageStats> stats = new ArrayList<>();
-        final UsageStats stat1 = new UsageStats();
-        final UsageStats stat2 = new UsageStats();
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "pkg.class";
-        stats.add(stat1);
-
-        stat2.mLastTimeUsed = System.currentTimeMillis();
-        stat2.mPackageName = "pkg.class2";
-        stats.add(stat2);
-
-        // app1 has AppEntry with null info, app2 has null AppEntry.
-        mAppEntry.info = null;
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId()))
-                .thenReturn(mAppEntry);
-        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId()))
-                .thenReturn(null);
-
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-
-        // We should not crash here.
-        mController.updateState(mRecentAppsPreference);
-    }
-
-    @Test
-    public void updateState_hiddenSystemModuleSet_shouldNotShowHiddenSystemModule() {
-        final List<UsageStats> stats = new ArrayList<>();
-        // Regular app.
-        final UsageStats stat1 = new UsageStats();
-        stat1.mLastTimeUsed = System.currentTimeMillis();
-        stat1.mPackageName = "com.foo.bar";
-        stats.add(stat1);
-
-        // Hidden system module.
-        final UsageStats stat2 = new UsageStats();
-        stat2.mLastTimeUsed = System.currentTimeMillis() + 200;
-        stat2.mPackageName = "com.foo.hidden";
-        stats.add(stat2);
-
-        ApplicationsState.AppEntry stat1Entry = mock(ApplicationsState.AppEntry.class);
-        ApplicationsState.AppEntry stat2Entry = mock(ApplicationsState.AppEntry.class);
-        stat1Entry.info = mApplicationInfo;
-        stat2Entry.info = mApplicationInfo;
-
-        when(mAppState.getEntry(stat1.mPackageName, UserHandle.myUserId())).thenReturn(stat1Entry);
-        when(mAppState.getEntry(stat2.mPackageName, UserHandle.myUserId())).thenReturn(stat2Entry);
-
-        final ModuleInfo moduleInfo1 = new ModuleInfo();
-        moduleInfo1.setPackageName(stat1.mPackageName);
-        moduleInfo1.setHidden(false);
-
-        final ModuleInfo moduleInfo2 = new ModuleInfo();
-        moduleInfo2.setPackageName(stat2.mPackageName);
-        moduleInfo2.setHidden(true);
-
-        ReflectionHelpers.setStaticField(ApplicationsState.class, "sInstance", null);
-        final List<ModuleInfo> modules = new ArrayList<>();
-        modules.add(moduleInfo2);
-        when(mPackageManager.getInstalledModules(anyInt() /* flags */))
-                .thenReturn(modules);
-
-        when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
-                .thenReturn(new ResolveInfo());
-        when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong()))
-                .thenReturn(stats);
-        mController.reloadData();
-
-        mController.updateState(mRecentAppsPreference);
-
-        // Only add stat1. stat2 is skipped because it is hidden module.
-        verify(mController.mAppEntitiesController).setAppEntity(anyInt(), any(AppEntityInfo.class));
-    }
-
-    // Used for matching an intent with a specific package name.
-    private static ArgumentMatcher<Intent> intentMatcher(String packageName) {
-        return intent -> packageName.equals(intent.getPackage());
+        assertThat(mController.mRecentAppsPreference.isVisible()).isFalse();
+        assertThat(mController.mDivider.isVisible()).isFalse();
     }
 }