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