1 /** 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.settings; 18 19 import android.app.Activity; 20 import android.app.usage.UsageStats; 21 import android.app.usage.UsageStatsManager; 22 import android.content.Context; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.PackageManager; 25 import android.content.pm.PackageManager.NameNotFoundException; 26 import android.os.Bundle; 27 import android.text.format.DateUtils; 28 import android.util.ArrayMap; 29 import android.util.Log; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 import android.widget.AdapterView; 34 import android.widget.AdapterView.OnItemSelectedListener; 35 import android.widget.BaseAdapter; 36 import android.widget.ListView; 37 import android.widget.Spinner; 38 import android.widget.TextView; 39 40 import java.text.DateFormat; 41 import java.util.ArrayList; 42 import java.util.Calendar; 43 import java.util.Collections; 44 import java.util.Comparator; 45 import java.util.List; 46 import java.util.Map; 47 48 /** 49 * Activity to display package usage statistics. 50 */ 51 public class UsageStatsActivity extends Activity implements OnItemSelectedListener { 52 private static final String TAG = "UsageStatsActivity"; 53 private static final boolean localLOGV = false; 54 private UsageStatsManager mUsageStatsManager; 55 private LayoutInflater mInflater; 56 private UsageStatsAdapter mAdapter; 57 private PackageManager mPm; 58 59 public static class AppNameComparator implements Comparator<UsageStats> { 60 private Map<String, String> mAppLabelList; 61 AppNameComparator(Map<String, String> appList)62 AppNameComparator(Map<String, String> appList) { 63 mAppLabelList = appList; 64 } 65 66 @Override compare(UsageStats a, UsageStats b)67 public final int compare(UsageStats a, UsageStats b) { 68 String alabel = mAppLabelList.get(a.getPackageName()); 69 String blabel = mAppLabelList.get(b.getPackageName()); 70 return alabel.compareTo(blabel); 71 } 72 } 73 74 public static class LastTimeUsedComparator implements Comparator<UsageStats> { 75 @Override compare(UsageStats a, UsageStats b)76 public final int compare(UsageStats a, UsageStats b) { 77 // return by descending order 78 return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed()); 79 } 80 } 81 82 public static class UsageTimeComparator implements Comparator<UsageStats> { 83 @Override compare(UsageStats a, UsageStats b)84 public final int compare(UsageStats a, UsageStats b) { 85 return Long.compare(b.getTotalTimeInForeground(), a.getTotalTimeInForeground()); 86 } 87 } 88 89 // View Holder used when displaying views 90 static class AppViewHolder { 91 TextView pkgName; 92 TextView lastTimeUsed; 93 TextView usageTime; 94 } 95 96 class UsageStatsAdapter extends BaseAdapter { 97 // Constants defining order for display order 98 private static final int _DISPLAY_ORDER_USAGE_TIME = 0; 99 private static final int _DISPLAY_ORDER_LAST_TIME_USED = 1; 100 private static final int _DISPLAY_ORDER_APP_NAME = 2; 101 102 private int mDisplayOrder = _DISPLAY_ORDER_USAGE_TIME; 103 private LastTimeUsedComparator mLastTimeUsedComparator = new LastTimeUsedComparator(); 104 private UsageTimeComparator mUsageTimeComparator = new UsageTimeComparator(); 105 private AppNameComparator mAppLabelComparator; 106 private final ArrayMap<String, String> mAppLabelMap = new ArrayMap<>(); 107 private final ArrayList<UsageStats> mPackageStats = new ArrayList<>(); 108 UsageStatsAdapter()109 UsageStatsAdapter() { 110 Calendar cal = Calendar.getInstance(); 111 cal.add(Calendar.DAY_OF_YEAR, -5); 112 113 final List<UsageStats> stats = 114 mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 115 cal.getTimeInMillis(), System.currentTimeMillis()); 116 if (stats == null) { 117 return; 118 } 119 120 ArrayMap<String, UsageStats> map = new ArrayMap<>(); 121 final int statCount = stats.size(); 122 for (int i = 0; i < statCount; i++) { 123 final android.app.usage.UsageStats pkgStats = stats.get(i); 124 125 // load application labels for each application 126 try { 127 ApplicationInfo appInfo = mPm.getApplicationInfo(pkgStats.getPackageName(), 0); 128 String label = appInfo.loadLabel(mPm).toString(); 129 mAppLabelMap.put(pkgStats.getPackageName(), label); 130 131 UsageStats existingStats = 132 map.get(pkgStats.getPackageName()); 133 if (existingStats == null) { 134 map.put(pkgStats.getPackageName(), pkgStats); 135 } else { 136 existingStats.add(pkgStats); 137 } 138 139 } catch (NameNotFoundException e) { 140 // This package may be gone. 141 } 142 } 143 mPackageStats.addAll(map.values()); 144 145 // Sort list 146 mAppLabelComparator = new AppNameComparator(mAppLabelMap); 147 sortList(); 148 } 149 150 @Override getCount()151 public int getCount() { 152 return mPackageStats.size(); 153 } 154 155 @Override getItem(int position)156 public Object getItem(int position) { 157 return mPackageStats.get(position); 158 } 159 160 @Override getItemId(int position)161 public long getItemId(int position) { 162 return position; 163 } 164 165 @Override getView(int position, View convertView, ViewGroup parent)166 public View getView(int position, View convertView, ViewGroup parent) { 167 // A ViewHolder keeps references to children views to avoid unneccessary calls 168 // to findViewById() on each row. 169 AppViewHolder holder; 170 171 // When convertView is not null, we can reuse it directly, there is no need 172 // to reinflate it. We only inflate a new View when the convertView supplied 173 // by ListView is null. 174 if (convertView == null) { 175 convertView = mInflater.inflate(R.layout.usage_stats_item, null); 176 177 // Creates a ViewHolder and store references to the two children views 178 // we want to bind data to. 179 holder = new AppViewHolder(); 180 holder.pkgName = (TextView) convertView.findViewById(R.id.package_name); 181 holder.lastTimeUsed = (TextView) convertView.findViewById(R.id.last_time_used); 182 holder.usageTime = (TextView) convertView.findViewById(R.id.usage_time); 183 convertView.setTag(holder); 184 } else { 185 // Get the ViewHolder back to get fast access to the TextView 186 // and the ImageView. 187 holder = (AppViewHolder) convertView.getTag(); 188 } 189 190 // Bind the data efficiently with the holder 191 UsageStats pkgStats = mPackageStats.get(position); 192 if (pkgStats != null) { 193 String label = mAppLabelMap.get(pkgStats.getPackageName()); 194 holder.pkgName.setText(label); 195 holder.lastTimeUsed.setText(DateUtils.formatSameDayTime(pkgStats.getLastTimeUsed(), 196 System.currentTimeMillis(), DateFormat.MEDIUM, DateFormat.MEDIUM)); 197 holder.usageTime.setText( 198 DateUtils.formatElapsedTime(pkgStats.getTotalTimeInForeground() / 1000)); 199 } else { 200 Log.w(TAG, "No usage stats info for package:" + position); 201 } 202 return convertView; 203 } 204 sortList(int sortOrder)205 void sortList(int sortOrder) { 206 if (mDisplayOrder == sortOrder) { 207 // do nothing 208 return; 209 } 210 mDisplayOrder= sortOrder; 211 sortList(); 212 } sortList()213 private void sortList() { 214 if (mDisplayOrder == _DISPLAY_ORDER_USAGE_TIME) { 215 if (localLOGV) Log.i(TAG, "Sorting by usage time"); 216 Collections.sort(mPackageStats, mUsageTimeComparator); 217 } else if (mDisplayOrder == _DISPLAY_ORDER_LAST_TIME_USED) { 218 if (localLOGV) Log.i(TAG, "Sorting by last time used"); 219 Collections.sort(mPackageStats, mLastTimeUsedComparator); 220 } else if (mDisplayOrder == _DISPLAY_ORDER_APP_NAME) { 221 if (localLOGV) Log.i(TAG, "Sorting by application name"); 222 Collections.sort(mPackageStats, mAppLabelComparator); 223 } 224 notifyDataSetChanged(); 225 } 226 } 227 228 /** Called when the activity is first created. */ 229 @Override onCreate(Bundle icicle)230 protected void onCreate(Bundle icicle) { 231 super.onCreate(icicle); 232 setContentView(R.layout.usage_stats); 233 234 mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE); 235 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); 236 mPm = getPackageManager(); 237 238 Spinner typeSpinner = (Spinner) findViewById(R.id.typeSpinner); 239 typeSpinner.setOnItemSelectedListener(this); 240 241 ListView listView = (ListView) findViewById(R.id.pkg_list); 242 mAdapter = new UsageStatsAdapter(); 243 listView.setAdapter(mAdapter); 244 } 245 246 @Override onItemSelected(AdapterView<?> parent, View view, int position, long id)247 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 248 mAdapter.sortList(position); 249 } 250 251 @Override onNothingSelected(AdapterView<?> parent)252 public void onNothingSelected(AdapterView<?> parent) { 253 // do nothing 254 } 255 } 256