1 /*
2  * Copyright (C) 2017 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.view.View;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.VisibleForTesting;
28 import androidx.fragment.app.Fragment;
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.R;
33 import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
34 import com.android.settings.applications.manageapplications.ManageApplications;
35 import com.android.settings.core.BasePreferenceController;
36 import com.android.settings.core.SubSettingLauncher;
37 import com.android.settings.overlay.FeatureFactory;
38 import com.android.settingslib.Utils;
39 import com.android.settingslib.applications.ApplicationsState;
40 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
41 import com.android.settingslib.utils.StringUtil;
42 import com.android.settingslib.widget.AppEntitiesHeaderController;
43 import com.android.settingslib.widget.AppEntityInfo;
44 import com.android.settingslib.widget.LayoutPreference;
45 
46 import java.util.List;
47 
48 /**
49  * This controller displays up to three recently used apps.
50  * If there is no recently used app, we only show up an "App Info" preference.
51  */
52 public class RecentAppsPreferenceController extends BasePreferenceController
53         implements RecentAppStatsMixin.RecentAppStatsListener {
54 
55     @VisibleForTesting
56     static final String KEY_DIVIDER = "recent_apps_divider";
57 
58     @VisibleForTesting
59     AppEntitiesHeaderController mAppEntitiesController;
60     @VisibleForTesting
61     LayoutPreference mRecentAppsPreference;
62     @VisibleForTesting
63     Preference mDivider;
64 
65     private final ApplicationsState mApplicationsState;
66     private final int mUserId;
67     private final MetricsFeatureProvider mMetricsFeatureProvider;
68 
69     private Fragment mHost;
70     private List<UsageStats> mRecentApps;
71 
RecentAppsPreferenceController(Context context, String key)72     public RecentAppsPreferenceController(Context context, String key) {
73         super(context, key);
74         mApplicationsState = ApplicationsState.getInstance(
75                 (Application) mContext.getApplicationContext());
76         mUserId = UserHandle.myUserId();
77         mMetricsFeatureProvider = FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
78     }
79 
setFragment(Fragment fragment)80     public void setFragment(Fragment fragment) {
81         mHost = fragment;
82     }
83 
84     @Override
getAvailabilityStatus()85     public int getAvailabilityStatus() {
86         return AVAILABLE_UNSEARCHABLE;
87     }
88 
89     @Override
displayPreference(PreferenceScreen screen)90     public void displayPreference(PreferenceScreen screen) {
91         super.displayPreference(screen);
92 
93         mDivider = screen.findPreference(KEY_DIVIDER);
94         mRecentAppsPreference = screen.findPreference(getPreferenceKey());
95         final View view = mRecentAppsPreference.findViewById(R.id.app_entities_header);
96         mAppEntitiesController = AppEntitiesHeaderController.newInstance(mContext, view)
97                 .setHeaderTitleRes(R.string.recent_app_category_title)
98                 .setHeaderDetailsClickListener((View v) -> {
99                     mMetricsFeatureProvider.logClickedPreference(mRecentAppsPreference,
100                             getMetricsCategory());
101                     new SubSettingLauncher(mContext)
102                             .setDestination(ManageApplications.class.getName())
103                             .setArguments(null /* arguments */)
104                             .setTitleRes(R.string.application_info_label)
105                             .setSourceMetricsCategory(getMetricsCategory())
106                             .launch();
107                 });
108     }
109 
110     @Override
onReloadDataCompleted(@onNull List<UsageStats> recentApps)111     public void onReloadDataCompleted(@NonNull List<UsageStats> recentApps) {
112         mRecentApps = recentApps;
113         refreshUi();
114         // Show total number of installed apps as See all's summary.
115         new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
116                 mContext.getPackageManager()) {
117             @Override
118             protected void onCountComplete(int num) {
119                 mAppEntitiesController.setHeaderDetails(
120                         mContext.getResources().getQuantityString(R.plurals.see_all_apps_title,
121                                 num, num));
122                 mAppEntitiesController.apply();
123             }
124         }.execute();
125     }
126 
refreshUi()127     private void refreshUi() {
128         if (!mRecentApps.isEmpty()) {
129             displayRecentApps();
130             mRecentAppsPreference.setVisible(true);
131             mDivider.setVisible(true);
132         } else {
133             mDivider.setVisible(false);
134             mRecentAppsPreference.setVisible(false);
135         }
136     }
137 
displayRecentApps()138     private void displayRecentApps() {
139         int showAppsCount = 0;
140 
141         for (UsageStats stat : mRecentApps) {
142             final AppEntityInfo appEntityInfoInfo = createAppEntity(stat);
143             if (appEntityInfoInfo != null) {
144                 mAppEntitiesController.setAppEntity(showAppsCount++, appEntityInfoInfo);
145             }
146 
147             if (showAppsCount == AppEntitiesHeaderController.MAXIMUM_APPS) {
148                 break;
149             }
150         }
151     }
152 
createAppEntity(UsageStats stat)153     private AppEntityInfo createAppEntity(UsageStats stat) {
154         final String pkgName = stat.getPackageName();
155         final ApplicationsState.AppEntry appEntry =
156                 mApplicationsState.getEntry(pkgName, mUserId);
157         if (appEntry == null) {
158             return null;
159         }
160 
161         return new AppEntityInfo.Builder()
162                 .setIcon(Utils.getBadgedIcon(mContext, appEntry.info))
163                 .setTitle(appEntry.label)
164                 .setSummary(StringUtil.formatRelativeTime(mContext,
165                         System.currentTimeMillis() - stat.getLastTimeUsed(), false,
166                         RelativeDateTimeFormatter.Style.SHORT))
167                 .setOnClickListener(v -> {
168                     mMetricsFeatureProvider.logClickedPreference(mRecentAppsPreference,
169                             getMetricsCategory());
170                     AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
171                             R.string.application_info_label, pkgName, appEntry.info.uid,
172                             mHost, 1001 /*RequestCode*/, getMetricsCategory());
173                 })
174                 .build();
175     }
176 }
177