1 /*
2  * Copyright (C) 2019 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.car.developeroptions.applications;
18 
19 import android.app.Application;
20 import android.app.settings.SettingsEnums;
21 import android.app.usage.UsageStats;
22 import android.content.Context;
23 import android.icu.text.RelativeDateTimeFormatter;
24 import android.os.UserHandle;
25 import android.util.IconDrawableFactory;
26 import android.util.Log;
27 import android.view.View;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.VisibleForTesting;
31 import androidx.fragment.app.Fragment;
32 import androidx.preference.Preference;
33 import androidx.preference.PreferenceScreen;
34 
35 import com.android.car.developeroptions.R;
36 import com.android.car.developeroptions.applications.appinfo.AppInfoDashboardFragment;
37 import com.android.car.developeroptions.applications.manageapplications.ManageApplications;
38 import com.android.car.developeroptions.core.BasePreferenceController;
39 import com.android.car.developeroptions.core.SubSettingLauncher;
40 import com.android.settingslib.applications.ApplicationsState;
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 IconDrawableFactory mIconDrawableFactory;
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         mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
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                     new SubSettingLauncher(mContext)
100                             .setDestination(ManageApplications.class.getName())
101                             .setArguments(null /* arguments */)
102                             .setTitleRes(R.string.application_info_label)
103                             .setSourceMetricsCategory(SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY)
104                             .launch();
105                 });
106     }
107 
108     @Override
onReloadDataCompleted(@onNull List<UsageStats> recentApps)109     public void onReloadDataCompleted(@NonNull List<UsageStats> recentApps) {
110         mRecentApps = recentApps;
111         refreshUi();
112         // Show total number of installed apps as See all's summary.
113         new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
114                 mContext.getPackageManager()) {
115             @Override
116             protected void onCountComplete(int num) {
117                 mAppEntitiesController.setHeaderDetails(
118                         mContext.getString(R.string.see_all_apps_title, num));
119                 mAppEntitiesController.apply();
120             }
121         }.execute();
122     }
123 
refreshUi()124     private void refreshUi() {
125         if (!mRecentApps.isEmpty()) {
126             displayRecentApps();
127             mRecentAppsPreference.setVisible(true);
128             mDivider.setVisible(true);
129         } else {
130             mDivider.setVisible(false);
131             mRecentAppsPreference.setVisible(false);
132         }
133     }
134 
displayRecentApps()135     private void displayRecentApps() {
136         int showAppsCount = 0;
137 
138         for (UsageStats stat : mRecentApps) {
139             final AppEntityInfo appEntityInfoInfo = createAppEntity(stat);
140             if (appEntityInfoInfo != null) {
141                 mAppEntitiesController.setAppEntity(showAppsCount++, appEntityInfoInfo);
142             }
143 
144             if (showAppsCount == AppEntitiesHeaderController.MAXIMUM_APPS) {
145                 break;
146             }
147         }
148     }
149 
createAppEntity(UsageStats stat)150     private AppEntityInfo createAppEntity(UsageStats stat) {
151         final String pkgName = stat.getPackageName();
152         final ApplicationsState.AppEntry appEntry =
153                 mApplicationsState.getEntry(pkgName, mUserId);
154         if (appEntry == null) {
155             return null;
156         }
157 
158         return new AppEntityInfo.Builder()
159                 .setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info))
160                 .setTitle(appEntry.label)
161                 .setSummary(StringUtil.formatRelativeTime(mContext,
162                         System.currentTimeMillis() - stat.getLastTimeUsed(), false,
163                         RelativeDateTimeFormatter.Style.SHORT))
164                 .setOnClickListener(v ->
165                         AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
166                                 R.string.application_info_label, pkgName, appEntry.info.uid,
167                                 mHost, 1001 /*RequestCode*/,
168                                 SettingsEnums.SETTINGS_APP_NOTIF_CATEGORY))
169                 .build();
170     }
171 }
172