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

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

Merge "Add app entities widget for recently opened apps"

parents 962cbf42 7de53146
Loading
Loading
Loading
Loading
+15 −14
Original line number Diff line number Diff line
@@ -23,24 +23,25 @@
    settings:initialExpandedChildrenCount="8">
    <!-- the initial count should include the dynamic tiles -->

    <PreferenceCategory
        android:key="recent_apps_category"
        android:title="@string/recent_app_category_title"
        android:order="-200">
        <!-- Placeholder for a list of recent apps -->

        <!-- See all apps button -->
    <Preference
            android:title="@string/applications_settings"
        android:key="all_app_info"
            android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
            android:order="20"/>
    </PreferenceCategory>
        android:title="@string/applications_settings"
        android:order="-999"
        android:fragment="com.android.settings.applications.manageapplications.ManageApplications"/>

    <com.android.settingslib.widget.LayoutPreference
        android:key="recent_open_apps"
        android:title="@string/recent_app_category_title"
        android:layout="@layout/app_entities_header"
        android:selectable="false"
        android:order="-998"
        settings:allowDividerBelow="true"
        settings:controller="com.android.settings.applications.RecentAppsPreferenceController"/>

    <!-- Empty category to draw divider -->
    <PreferenceCategory
        android:key="all_app_info_divider"
        android:order="-190"/>
        android:key="recent_apps_divider"
        android:order="-997"/>

    <!-- Notifications (appears before manage_perms), default apps (appears after) -->
    <PreferenceCategory
+5 −16
Original line number Diff line number Diff line
@@ -16,14 +16,10 @@

package com.android.settings.applications;

import android.app.Activity;
import android.app.Application;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.provider.SearchIndexableResource;

import androidx.fragment.app.Fragment;

import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.EmergencyBroadcastPreferenceController;
@@ -63,27 +59,20 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment {
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);

        use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
        use(RecentAppsPreferenceController.class).setFragment(this /* fragment */);
    }

    @Override
    protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
        final Activity activity = getActivity();
        final Application app;
        if (activity != null) {
            app = activity.getApplication();
        } else {
            app = null;
        }
        return buildPreferenceControllers(context, app, this);
        return buildPreferenceControllers(context);
    }

    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
            Application app, Fragment host) {
    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
        final List<AbstractPreferenceController> controllers = new ArrayList<>();
        controllers.add(new EmergencyBroadcastPreferenceController(context,
                "app_and_notif_cell_broadcast_settings"));
        controllers.add(new RecentAppsPreferenceController(context, app, host));
        return controllers;
    }

@@ -100,7 +89,7 @@ public class AppAndNotificationDashboardFragment extends DashboardFragment {
                @Override
                public List<AbstractPreferenceController> createPreferenceControllers(
                        Context context) {
                    return buildPreferenceControllers(context, null, null /* host */);
                    return buildPreferenceControllers(context);
                }
            };
}
+99 −117
Original line number Diff line number Diff line
@@ -27,26 +27,28 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.View;

import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
import com.android.settings.core.PreferenceControllerMixin;
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.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.apppreference.AppPreference;
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;
@@ -58,22 +60,29 @@ import java.util.Map;
import java.util.Set;

/**
 * This controller displays a list of recently used apps and a "See all" button. If there is
 * no recently used app, "See all" will be displayed as "App info".
 * 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 AbstractPreferenceController
        implements PreferenceControllerMixin, Comparator<UsageStats> {
public class RecentAppsPreferenceController extends BasePreferenceController
        implements Comparator<UsageStats> {

    private static final String TAG = "RecentAppsCtrl";
    private static final String KEY_PREF_CATEGORY = "recent_apps_category";
    @VisibleForTesting
    static final String KEY_DIVIDER = "all_app_info_divider";
    static final String KEY_ALL_APP_INFO = "all_app_info";
    @VisibleForTesting
    static final String KEY_SEE_ALL = "all_app_info";
    private static final int SHOW_RECENT_APP_COUNT = 5;
    static final String KEY_DIVIDER = "recent_apps_divider";

    private static final String TAG = "RecentAppsCtrl";
    private static final Set<String> SKIP_SYSTEM_PACKAGES = new ArraySet<>();

    private final Fragment mHost;
    @VisibleForTesting
    AppEntitiesHeaderController mAppEntitiesController;
    @VisibleForTesting
    LayoutPreference mRecentAppsPreference;
    @VisibleForTesting
    Preference mAllAppPref;
    @VisibleForTesting
    Preference mDivider;

    private final PackageManager mPm;
    private final UsageStatsManager mUsageStatsManager;
    private final ApplicationsState mApplicationsState;
@@ -81,12 +90,9 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
    private final IconDrawableFactory mIconDrawableFactory;
    private final PowerManager mPowerManager;

    private Fragment mHost;
    private Calendar mCal;
    private List<UsageStats> mStats;

    private PreferenceCategory mCategory;
    private Preference mSeeAllPref;
    private Preference mDivider;
    private boolean mHasRecentApps;

    static {
@@ -100,68 +106,67 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
        ));
    }

    public RecentAppsPreferenceController(Context context, Application app, Fragment host) {
        this(context, app == null ? null : ApplicationsState.getInstance(app), host);
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    RecentAppsPreferenceController(Context context, ApplicationsState appState, Fragment host) {
        super(context);
        mIconDrawableFactory = IconDrawableFactory.newInstance(context);
    public RecentAppsPreferenceController(Context context, String key) {
        super(context, key);
        mApplicationsState = ApplicationsState.getInstance(
                (Application) mContext.getApplicationContext());
        mUserId = UserHandle.myUserId();
        mPm = context.getPackageManager();
        mPowerManager = context.getSystemService(PowerManager.class);

        mHost = host;
        mUsageStatsManager =
                (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE);
        mApplicationsState = appState;
    }

    @Override
    public boolean isAvailable() {
        return true;
        mPm = mContext.getPackageManager();
        mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
        mPowerManager = mContext.getSystemService(PowerManager.class);
        mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
    }

    @Override
    public String getPreferenceKey() {
        return KEY_PREF_CATEGORY;
    public void setFragment(Fragment fragment) {
        mHost = fragment;
    }

    @Override
    public void updateNonIndexableKeys(List<String> keys) {
        PreferenceControllerMixin.super.updateNonIndexableKeys(keys);
        // Don't index category name into search. It's not actionable.
        keys.add(KEY_PREF_CATEGORY);
        keys.add(KEY_DIVIDER);
    public int getAvailabilityStatus() {
        reloadData();
        return getDisplayableRecentAppList().isEmpty() ? AVAILABLE_UNSEARCHABLE : AVAILABLE;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        mCategory = screen.findPreference(getPreferenceKey());
        mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
        mDivider = screen.findPreference(KEY_DIVIDER);
        super.displayPreference(screen);
        refreshUi(mCategory.getContext());

        mAllAppPref = screen.findPreference(KEY_ALL_APP_INFO);
        mDivider = screen.findPreference(KEY_DIVIDER);
        mRecentAppsPreference = (LayoutPreference) 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)
                .setHeaderDetailsClickListener((View v) -> {
                    new SubSettingLauncher(mContext)
                            .setDestination(ManageApplications.class.getName())
                            .setArguments(null /* arguments */)
                            .setTitleRes(R.string.application_info_label)
                            .setSourceMetricsCategory(SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY)
                            .launch();
                });

        refreshUi();
    }

    @Override
    public void updateState(Preference preference) {
        super.updateState(preference);
        refreshUi(mCategory.getContext());
        refreshUi();
        // 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) {
                if (mHasRecentApps) {
                    mSeeAllPref.setTitle(mContext.getString(R.string.see_all_apps_title, num));
                    mAppEntitiesController.setHeaderDetails(
                            mContext.getString(R.string.see_all_apps_title, num));
                    mAppEntitiesController.apply();
                } else {
                    mSeeAllPref.setSummary(mContext.getString(R.string.apps_summary, num));
                    mAllAppPref.setSummary(mContext.getString(R.string.apps_summary, num));
                }
            }
        }.execute();

    }

    @Override
@@ -171,12 +176,12 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
    }

    @VisibleForTesting
    void refreshUi(Context prefContext) {
    void refreshUi() {
        reloadData();
        final List<UsageStats> recentApps = getDisplayableRecentAppList();
        if (recentApps != null && !recentApps.isEmpty()) {
            mHasRecentApps = true;
            displayRecentApps(prefContext, recentApps);
            displayRecentApps(recentApps);
        } else {
            mHasRecentApps = false;
            displayOnlyAppInfo();
@@ -195,73 +200,50 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
    }

    private void displayOnlyAppInfo() {
        mCategory.setTitle(null);
        mDivider.setVisible(false);
        mSeeAllPref.setTitle(R.string.applications_settings);
        mSeeAllPref.setIcon(null);
        int prefCount = mCategory.getPreferenceCount();
        for (int i = prefCount - 1; i >= 0; i--) {
            final Preference pref = mCategory.getPreference(i);
            if (!TextUtils.equals(pref.getKey(), KEY_SEE_ALL)) {
                mCategory.removePreference(pref);
            }
        mAllAppPref.setTitle(R.string.applications_settings);
        mAllAppPref.setVisible(true);
        mRecentAppsPreference.setVisible(false);
    }

    private void displayRecentApps(List<UsageStats> recentApps) {
        int showAppsCount = 0;

        for (UsageStats stat : recentApps) {
            final AppEntityInfo appEntityInfoInfo = createAppEntity(stat);
            if (appEntityInfoInfo != null) {
                mAppEntitiesController.setAppEntity(showAppsCount++, appEntityInfoInfo);
            }

    private void displayRecentApps(Context prefContext, List<UsageStats> recentApps) {
        mCategory.setTitle(R.string.recent_app_category_title);
            if (showAppsCount == AppEntitiesHeaderController.MAXIMUM_APPS) {
                break;
            }
        }
        mAppEntitiesController.apply();
        mRecentAppsPreference.setVisible(true);
        mAllAppPref.setVisible(false);
        mDivider.setVisible(true);
        mSeeAllPref.setSummary(null);
        mSeeAllPref.setIcon(R.drawable.ic_chevron_right_24dp);

        // Rebind prefs/avoid adding new prefs if possible. Adding/removing prefs causes jank.
        // Build a cached preference pool
        final Map<String, Preference> appPreferences = new ArrayMap<>();
        int prefCount = mCategory.getPreferenceCount();
        for (int i = 0; i < prefCount; i++) {
            final Preference pref = mCategory.getPreference(i);
            final String key = pref.getKey();
            if (!TextUtils.equals(key, KEY_SEE_ALL)) {
                appPreferences.put(key, pref);
            }
        }
        final int recentAppsCount = recentApps.size();
        for (int i = 0; i < recentAppsCount; i++) {
            final UsageStats stat = recentApps.get(i);
            // Bind recent apps to existing prefs if possible, or create a new pref.
    }

    private AppEntityInfo createAppEntity(UsageStats stat) {
        final String pkgName = stat.getPackageName();
        final ApplicationsState.AppEntry appEntry =
                mApplicationsState.getEntry(pkgName, mUserId);
        if (appEntry == null) {
                continue;
            return null;
        }

            boolean rebindPref = true;
            Preference pref = appPreferences.remove(pkgName);
            if (pref == null) {
                pref = new AppPreference(prefContext);
                rebindPref = false;
            }
            pref.setKey(pkgName);
            pref.setTitle(appEntry.label);
            pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info));
            pref.setSummary(StringUtil.formatRelativeTime(mContext,
                    System.currentTimeMillis() - stat.getLastTimeUsed(), false));
            pref.setOrder(i);
            pref.setOnPreferenceClickListener(preference -> {
        return new AppEntityInfo.Builder()
                .setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info))
                .setTitle(appEntry.label)
                .setSummary(StringUtil.formatRelativeTime(mContext,
                        System.currentTimeMillis() - stat.getLastTimeUsed(), false))
                .setOnClickListener(v ->
                        AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
                        R.string.application_info_label, pkgName, appEntry.info.uid, mHost,
                        1001 /*RequestCode*/, SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY);
                return true;
            });
            if (!rebindPref) {
                mCategory.addPreference(pref);
            }
        }
        // Remove unused prefs from pref cache pool
        for (Preference unusedPrefs : appPreferences.values()) {
            mCategory.removePreference(unusedPrefs);
        }
                                R.string.application_info_label, pkgName, appEntry.info.uid,
                                mHost, 1001 /*RequestCode*/,
                                SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY))
                .build();
    }

    private List<UsageStats> getDisplayableRecentAppList() {
@@ -293,7 +275,7 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController
            }
            recentApps.add(stat);
            count++;
            if (count >= SHOW_RECENT_APP_COUNT) {
            if (count >= AppEntitiesHeaderController.MAXIMUM_APPS) {
                break;
            }
        }
+128 −141

File changed.

Preview size limit exceeded, changes collapsed.