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