• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import android.app.Application;
20 import android.app.usage.UsageStats;
21 import android.content.Context;
22 import android.icu.text.RelativeDateTimeFormatter;
23 import android.os.UserHandle;
24 import android.text.TextUtils;
25 import android.util.ArrayMap;
26 
27 import androidx.annotation.VisibleForTesting;
28 import androidx.fragment.app.Fragment;
29 import androidx.lifecycle.Lifecycle;
30 import androidx.lifecycle.LifecycleObserver;
31 import androidx.lifecycle.OnLifecycleEvent;
32 import androidx.preference.Preference;
33 import androidx.preference.PreferenceCategory;
34 import androidx.preference.PreferenceScreen;
35 
36 import com.android.settings.R;
37 import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
38 import com.android.settings.core.BasePreferenceController;
39 import com.android.settingslib.Utils;
40 import com.android.settingslib.applications.ApplicationsState;
41 import com.android.settingslib.utils.StringUtil;
42 import com.android.settingslib.widget.AppPreference;
43 
44 import java.util.List;
45 import java.util.Map;
46 
47 /**
48  * This controller displays up to four recently used apps.
49  * If there is no recently used app, we only show up an "App Info" preference.
50  */
51 public class AppsPreferenceController extends BasePreferenceController implements
52         LifecycleObserver {
53 
54     public static final int SHOW_RECENT_APP_COUNT = 4;
55 
56     @VisibleForTesting
57     static final String KEY_RECENT_APPS_CATEGORY = "recent_apps_category";
58     @VisibleForTesting
59     static final String KEY_GENERAL_CATEGORY = "general_category";
60     @VisibleForTesting
61     static final String KEY_ALL_APP_INFO = "all_app_infos";
62     @VisibleForTesting
63     static final String KEY_SEE_ALL = "see_all_apps";
64 
65     private final ApplicationsState mApplicationsState;
66     private final int mUserId;
67 
68     @VisibleForTesting
69     List<UsageStats> mRecentApps;
70     @VisibleForTesting
71     PreferenceCategory mRecentAppsCategory;
72     @VisibleForTesting
73     PreferenceCategory mGeneralCategory;
74     @VisibleForTesting
75     Preference mAllAppsInfoPref;
76     @VisibleForTesting
77     Preference mSeeAllPref;
78 
79     private Fragment mHost;
80     private boolean mInitialLaunch = false;
81 
AppsPreferenceController(Context context)82     public AppsPreferenceController(Context context) {
83         super(context, KEY_RECENT_APPS_CATEGORY);
84         mApplicationsState = ApplicationsState.getInstance(
85                 (Application) mContext.getApplicationContext());
86         mUserId = UserHandle.myUserId();
87     }
88 
setFragment(Fragment fragment)89     public void setFragment(Fragment fragment) {
90         mHost = fragment;
91     }
92 
93     @Override
getAvailabilityStatus()94     public int getAvailabilityStatus() {
95         return AVAILABLE_UNSEARCHABLE;
96     }
97 
98     @Override
displayPreference(PreferenceScreen screen)99     public void displayPreference(PreferenceScreen screen) {
100         super.displayPreference(screen);
101         initPreferences(screen);
102         refreshUi();
103         mInitialLaunch = true;
104     }
105 
106     @Override
updateState(Preference preference)107     public void updateState(Preference preference) {
108         super.updateState(preference);
109         if (!mInitialLaunch) {
110             refreshUi();
111         }
112     }
113 
114     /**
115      * Called when the apps page pauses.
116      */
117     @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
onPause()118     public void onPause() {
119         mInitialLaunch = false;
120     }
121 
122     @VisibleForTesting
refreshUi()123     void refreshUi() {
124         loadAllAppsCount();
125         mRecentApps = loadRecentApps();
126         if (!mRecentApps.isEmpty()) {
127             displayRecentApps();
128             mAllAppsInfoPref.setVisible(false);
129             mRecentAppsCategory.setVisible(true);
130             mGeneralCategory.setVisible(true);
131             mSeeAllPref.setVisible(true);
132         } else {
133             mAllAppsInfoPref.setVisible(true);
134             mRecentAppsCategory.setVisible(false);
135             mGeneralCategory.setVisible(false);
136             mSeeAllPref.setVisible(false);
137         }
138     }
139 
140     @VisibleForTesting
loadAllAppsCount()141     void loadAllAppsCount() {
142         // Show total number of installed apps as See all's summary.
143         new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
144                 mContext.getPackageManager()) {
145             @Override
146             protected void onCountComplete(int num) {
147                 if (!mRecentApps.isEmpty()) {
148                     mSeeAllPref.setTitle(
149                             mContext.getResources().getQuantityString(R.plurals.see_all_apps_title,
150                                     num, num));
151                 } else {
152                     mAllAppsInfoPref.setSummary(mContext.getString(R.string.apps_summary, num));
153                 }
154             }
155         }.execute();
156     }
157 
158     @VisibleForTesting
loadRecentApps()159     List<UsageStats> loadRecentApps() {
160         final RecentAppStatsMixin recentAppStatsMixin = new RecentAppStatsMixin(mContext,
161                 SHOW_RECENT_APP_COUNT);
162         recentAppStatsMixin.loadDisplayableRecentApps(SHOW_RECENT_APP_COUNT);
163         return recentAppStatsMixin.mRecentApps;
164     }
165 
initPreferences(PreferenceScreen screen)166     private void initPreferences(PreferenceScreen screen) {
167         mRecentAppsCategory = screen.findPreference(KEY_RECENT_APPS_CATEGORY);
168         mGeneralCategory = screen.findPreference(KEY_GENERAL_CATEGORY);
169         mAllAppsInfoPref = screen.findPreference(KEY_ALL_APP_INFO);
170         mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
171         mRecentAppsCategory.setVisible(false);
172         mGeneralCategory.setVisible(false);
173         mAllAppsInfoPref.setVisible(false);
174         mSeeAllPref.setVisible(false);
175     }
176 
displayRecentApps()177     private void displayRecentApps() {
178         if (mRecentAppsCategory != null) {
179             final Map<String, Preference> existedAppPreferences = new ArrayMap<>();
180             final int prefCount = mRecentAppsCategory.getPreferenceCount();
181             for (int i = 0; i < prefCount; i++) {
182                 final Preference pref = mRecentAppsCategory.getPreference(i);
183                 final String key = pref.getKey();
184                 if (!TextUtils.equals(key, KEY_SEE_ALL)) {
185                     existedAppPreferences.put(key, pref);
186                 }
187             }
188 
189             int showAppsCount = 0;
190             for (UsageStats stat : mRecentApps) {
191                 final String pkgName = stat.getPackageName();
192                 final ApplicationsState.AppEntry appEntry =
193                         mApplicationsState.getEntry(pkgName, mUserId);
194                 if (appEntry == null) {
195                     continue;
196                 }
197 
198                 boolean rebindPref = true;
199                 Preference pref = existedAppPreferences.remove(pkgName);
200                 if (pref == null) {
201                     pref = new AppPreference(mContext);
202                     rebindPref = false;
203                 }
204 
205                 pref.setKey(pkgName);
206                 pref.setTitle(appEntry.label);
207                 pref.setIcon(Utils.getBadgedIcon(mContext, appEntry.info));
208                 pref.setSummary(StringUtil.formatRelativeTime(mContext,
209                         System.currentTimeMillis() - stat.getLastTimeUsed(), false,
210                         RelativeDateTimeFormatter.Style.SHORT));
211                 pref.setOrder(showAppsCount++);
212                 pref.setOnPreferenceClickListener(preference -> {
213                     AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
214                             R.string.application_info_label, pkgName, appEntry.info.uid,
215                             mHost, 1001 /*RequestCode*/, getMetricsCategory());
216                     return true;
217                 });
218 
219                 if (!rebindPref) {
220                     mRecentAppsCategory.addPreference(pref);
221                 }
222             }
223 
224             // Remove unused preferences from pref category.
225             for (Preference unusedPref : existedAppPreferences.values()) {
226                 mRecentAppsCategory.removePreference(unusedPref);
227             }
228         }
229     }
230 }
231