OSDN Git Service

c4b6db4779cbd18d829ffb5b7c8d588769431e3f
[android-x86/packages-apps-Settings.git] / src / com / android / settings / fuelgauge / PowerUsageDetail.java
1 /*
2  * Copyright (C) 2009 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.fuelgauge;
18
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.app.ApplicationErrorReport;
22 import android.app.Fragment;
23 import android.content.BroadcastReceiver;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.PackageManager.NameNotFoundException;
31 import android.content.res.Resources;
32 import android.graphics.drawable.Drawable;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.Process;
36 import android.preference.PreferenceActivity;
37 import android.provider.Settings;
38 import android.text.TextUtils;
39 import android.view.LayoutInflater;
40 import android.view.View;
41 import android.view.ViewGroup;
42 import android.widget.Button;
43 import android.widget.ImageView;
44 import android.widget.TextView;
45 import com.android.settings.R;
46 import com.android.settings.applications.InstalledAppDetails;
47
48 public class PowerUsageDetail extends Fragment implements Button.OnClickListener {
49
50     enum DrainType {
51         IDLE,
52         CELL,
53         PHONE,
54         WIFI,
55         BLUETOOTH,
56         SCREEN,
57         APP
58     }
59
60     // Note: Must match the sequence of the DrainType
61     private static int[] sDrainTypeDesciptions = new int[] {
62         R.string.battery_desc_standby,
63         R.string.battery_desc_radio,
64         R.string.battery_desc_voice,
65         R.string.battery_desc_wifi,
66         R.string.battery_desc_bluetooth,
67         R.string.battery_desc_display,
68         R.string.battery_desc_apps
69     };
70
71     public static final int ACTION_DISPLAY_SETTINGS = 1;
72     public static final int ACTION_WIFI_SETTINGS = 2;
73     public static final int ACTION_BLUETOOTH_SETTINGS = 3;
74     public static final int ACTION_WIRELESS_SETTINGS = 4;
75     public static final int ACTION_APP_DETAILS = 5;
76     public static final int ACTION_SECURITY_SETTINGS = 6;
77     public static final int ACTION_FORCE_STOP = 7;
78     public static final int ACTION_REPORT = 8;
79
80     public static final int USAGE_SINCE_UNPLUGGED = 1;
81     public static final int USAGE_SINCE_RESET = 2;
82
83     public static final String EXTRA_TITLE = "title";
84     public static final String EXTRA_PERCENT = "percent";
85     public static final String EXTRA_GAUGE = "gauge";
86     public static final String EXTRA_UID = "uid";
87     public static final String EXTRA_USAGE_SINCE = "since";
88     public static final String EXTRA_USAGE_DURATION = "duration";
89     public static final String EXTRA_REPORT_DETAILS = "report_details";
90     public static final String EXTRA_REPORT_CHECKIN_DETAILS = "report_checkin_details";
91     public static final String EXTRA_DETAIL_TYPES = "types"; // Array of usage types (cpu, gps, etc)
92     public static final String EXTRA_DETAIL_VALUES = "values"; // Array of doubles
93     public static final String EXTRA_DRAIN_TYPE = "drainType"; // DrainType
94     public static final String EXTRA_ICON_PACKAGE = "iconPackage"; // String
95     public static final String EXTRA_NO_COVERAGE = "noCoverage";
96     public static final String EXTRA_ICON_ID = "iconId"; // Int
97
98     private static final boolean DEBUG = true;
99     private String mTitle;
100     private int mUsageSince;
101     private int[] mTypes;
102     private int mUid;
103     private double[] mValues;
104     private View mRootView;
105     private TextView mTitleView;
106     private ViewGroup mTwoButtonsPanel;
107     private Button mForceStopButton;
108     private Button mReportButton;
109     private ViewGroup mDetailsParent;
110     private ViewGroup mControlsParent;
111     private long mStartTime;
112     private DrainType mDrainType;
113     private PercentageBar mGauge;
114     private Drawable mAppIcon;
115     private double mNoCoverage; // Percentage of time that there was no coverage
116
117     private boolean mUsesGps;
118
119     private static final String TAG = "PowerUsageDetail";
120     private String[] mPackages;
121
122     ApplicationInfo mApp;
123     ComponentName mInstaller;
124     
125     @Override
126     public void onCreate(Bundle icicle) {
127         super.onCreate(icicle);
128     }
129
130     @Override
131     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
132         View view = mRootView = inflater.inflate(R.layout.power_usage_details, null);
133         createDetails();
134         return view;
135     }
136
137     @Override
138     public void onResume() {
139         super.onResume();
140         mStartTime = android.os.Process.getElapsedCpuTime();
141         checkForceStop();
142     }
143
144     @Override
145     public void onPause() {
146         super.onPause();
147     }
148
149     private void createDetails() {
150         final Bundle args = getArguments();
151         mTitle = args.getString(EXTRA_TITLE);
152         final int percentage = args.getInt(EXTRA_PERCENT, 1);
153         final int gaugeValue = args.getInt(EXTRA_GAUGE, 1);
154         mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED);
155         mUid = args.getInt(EXTRA_UID, 0);
156         mDrainType = (DrainType) args.getSerializable(EXTRA_DRAIN_TYPE);
157         mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0);
158         String iconPackage = args.getString(EXTRA_ICON_PACKAGE);
159         int iconId = args.getInt(EXTRA_ICON_ID, 0);
160         if (!TextUtils.isEmpty(iconPackage)) {
161             try {
162                 final PackageManager pm = getActivity().getPackageManager();
163                 ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo;
164                 if (ai != null) {
165                     mAppIcon = ai.loadIcon(pm);
166                 }
167             } catch (NameNotFoundException nnfe) {
168                 // Use default icon
169             }
170         } else if (iconId != 0) {
171             mAppIcon = getActivity().getResources().getDrawable(iconId);
172         }
173         if (mAppIcon == null) {
174             mAppIcon = getActivity().getPackageManager().getDefaultActivityIcon();
175         }
176
177         // Set the description
178         String summary = getDescriptionForDrainType();
179         ((TextView)mRootView.findViewById(R.id.summary)).setText(summary);
180         
181         mTypes = args.getIntArray(EXTRA_DETAIL_TYPES);
182         mValues = args.getDoubleArray(EXTRA_DETAIL_VALUES);
183
184         mTitleView = (TextView)mRootView.findViewById(R.id.name);
185         mTitleView.setText(mTitle);
186         ((TextView)mRootView.findViewById(R.id.battery_percentage))
187             .setText(String.format("%d%%", percentage));
188
189         mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel);
190         mForceStopButton = (Button)mRootView.findViewById(R.id.left_button);
191         mReportButton = (Button)mRootView.findViewById(R.id.right_button);
192         mForceStopButton.setEnabled(false);
193         
194         ImageView gaugeImage = (ImageView)mRootView.findViewById(R.id.gauge);
195         mGauge = new PercentageBar();
196         mGauge.percent = gaugeValue;
197         mGauge.bar = getResources().getDrawable(R.drawable.app_gauge);
198         gaugeImage.setImageDrawable(mGauge);
199
200         ImageView iconImage = (ImageView)mRootView.findViewById(R.id.icon);
201         iconImage.setImageDrawable(mAppIcon);
202
203         mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
204         mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls);
205
206         fillDetailsSection();
207         fillPackagesSection(mUid);
208         fillControlsSection(mUid);
209         
210         if (mUid >= Process.FIRST_APPLICATION_UID) {
211             mForceStopButton.setText(R.string.force_stop);
212             mForceStopButton.setTag(ACTION_FORCE_STOP);
213             mForceStopButton.setOnClickListener(this);
214             mReportButton.setText(com.android.internal.R.string.report);
215             mReportButton.setTag(ACTION_REPORT);
216             mReportButton.setOnClickListener(this);
217             
218             // check if error reporting is enabled in secure settings
219             int enabled = Settings.Secure.getInt(getActivity().getContentResolver(),
220                     Settings.Secure.SEND_ACTION_APP_ERROR, 0);
221             if (enabled != 0) {
222                 if (mPackages != null && mPackages.length > 0) {
223                     try {
224                         mApp = getActivity().getPackageManager().getApplicationInfo(
225                                 mPackages[0], 0);
226                         mInstaller = ApplicationErrorReport.getErrorReportReceiver(
227                                 getActivity(), mPackages[0], mApp.flags);
228                     } catch (NameNotFoundException e) {
229                     }
230                 }
231                 mReportButton.setEnabled(mInstaller != null);
232             } else {
233                 mTwoButtonsPanel.setVisibility(View.GONE);
234             }
235         } else {
236             mTwoButtonsPanel.setVisibility(View.GONE);
237         }
238     }
239
240     public void onClick(View v) {
241         doAction((Integer) v.getTag());
242     }
243
244     // utility method used to start sub activity
245     private void startApplicationDetailsActivity() {
246         // start new fragment to display extended information
247         Bundle args = new Bundle();
248         args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mPackages[0]);
249
250         PreferenceActivity pa = (PreferenceActivity)getActivity();
251         pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
252                 R.string.application_info_label, null, null, 0);
253     }
254
255     private void doAction(int action) {
256         switch (action) {
257             case ACTION_DISPLAY_SETTINGS:
258                 startActivity(new Intent(Settings.ACTION_DISPLAY_SETTINGS));
259                 break;
260             case ACTION_WIFI_SETTINGS:
261                 startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
262                 break;
263             case ACTION_BLUETOOTH_SETTINGS:
264                 startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
265                 break;
266             case ACTION_WIRELESS_SETTINGS:
267                 startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
268                 break;
269             case ACTION_APP_DETAILS:
270                 startApplicationDetailsActivity();
271                 break;
272             case ACTION_SECURITY_SETTINGS:
273                 startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
274                 break;
275             case ACTION_FORCE_STOP:
276                 killProcesses();
277                 break;
278             case ACTION_REPORT:
279                 reportBatteryUse();
280                 break;
281         }
282     }
283
284     private void fillDetailsSection() {
285         LayoutInflater inflater = getActivity().getLayoutInflater();
286         if (mTypes != null && mValues != null) {
287             for (int i = 0; i < mTypes.length; i++) {
288                 // Only add an item if the time is greater than zero
289                 if (mValues[i] <= 0) continue;
290                 final String label = getString(mTypes[i]);
291                 String value = null;
292                 switch (mTypes[i]) {
293                     case R.string.usage_type_data_recv:
294                     case R.string.usage_type_data_send:
295                         value = Utils.formatBytes(getActivity(), mValues[i]);
296                         break;
297                     case R.string.usage_type_no_coverage:
298                         value = String.format("%d%%", (int) Math.floor(mValues[i]));
299                         break;
300                     case R.string.usage_type_gps:
301                         mUsesGps = true;
302                         // Fall through
303                     default:
304                         value = Utils.formatElapsedTime(getActivity(), mValues[i]);
305                 }
306                 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
307                         null);
308                 mDetailsParent.addView(item);
309                 TextView labelView = (TextView) item.findViewById(R.id.label);
310                 TextView valueView = (TextView) item.findViewById(R.id.value);
311                 labelView.setText(label);
312                 valueView.setText(value);
313             }
314         }
315     }
316
317     private void fillControlsSection(int uid) {
318         PackageManager pm = getActivity().getPackageManager();
319         String[] packages = pm.getPackagesForUid(uid);
320         PackageInfo pi = null;
321         try {
322             pi = packages != null ? pm.getPackageInfo(packages[0], 0) : null;
323         } catch (NameNotFoundException nnfe) { /* Nothing */ }
324         ApplicationInfo ai = pi != null? pi.applicationInfo : null;
325         boolean isSystem = ai != null? (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0 : false;
326
327         boolean removeHeader = true;
328         switch (mDrainType) {
329             case APP:
330                 // If it is a Java application and it's not a system application
331                 if (packages != null && !isSystem) {
332                     addControl(R.string.battery_action_app_details,
333                             R.string.battery_sugg_apps_info, ACTION_APP_DETAILS);
334                     removeHeader = false;
335                     // If the application has a settings screen, jump to  that
336                     // TODO:
337                 }
338                 if (mUsesGps) {
339                     addControl(R.string.security_settings_title,
340                             R.string.battery_sugg_apps_gps, ACTION_SECURITY_SETTINGS);
341                     removeHeader = false;
342                 }
343                 break;
344             case SCREEN:
345                 addControl(R.string.display_settings,
346                         R.string.battery_sugg_display,
347                         ACTION_DISPLAY_SETTINGS);
348                 removeHeader = false;
349                 break;
350             case WIFI:
351                 addControl(R.string.wifi_settings,
352                         R.string.battery_sugg_wifi,
353                         ACTION_WIFI_SETTINGS);
354                 removeHeader = false;
355                 break;
356             case BLUETOOTH:
357                 addControl(R.string.bluetooth_settings,
358                         R.string.battery_sugg_bluetooth_basic,
359                         ACTION_BLUETOOTH_SETTINGS);
360                 removeHeader = false;
361                 break;
362             case CELL:
363                 if (mNoCoverage > 10) {
364                     addControl(R.string.radio_controls_title,
365                             R.string.battery_sugg_radio,
366                             ACTION_WIRELESS_SETTINGS);
367                     removeHeader = false;
368                 }
369                 break;
370         }
371         if (removeHeader) {
372             mControlsParent.setVisibility(View.GONE);
373         }
374     }
375
376     private void addControl(int title, int summary, int action) {
377         final Resources res = getResources();
378         LayoutInflater inflater = getActivity().getLayoutInflater();
379         ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_action_item,null);
380         mControlsParent.addView(item);
381         Button actionButton = (Button) item.findViewById(R.id.action_button);
382         TextView summaryView = (TextView) item.findViewById(R.id.summary);
383         actionButton.setText(res.getString(title));
384         summaryView.setText(res.getString(summary));
385         actionButton.setOnClickListener(this);
386         actionButton.setTag(new Integer(action));
387     }
388
389     private void removePackagesSection() {
390         View view;
391         if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) {
392             view.setVisibility(View.GONE);
393         }
394         if ((view = mRootView.findViewById(R.id.packages_section)) != null) {
395             view.setVisibility(View.GONE);
396         }
397     }
398
399     private void killProcesses() {
400         if (mPackages == null) return;
401         ActivityManager am = (ActivityManager)getActivity().getSystemService(
402                 Context.ACTIVITY_SERVICE);
403         for (int i = 0; i < mPackages.length; i++) {
404             am.forceStopPackage(mPackages[i]);
405         }
406         checkForceStop();
407     }
408
409     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
410         @Override
411         public void onReceive(Context context, Intent intent) {
412             mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
413         }
414     };
415     
416     private void checkForceStop() {
417         if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) {
418             mForceStopButton.setEnabled(false);
419             return;
420         }
421         Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
422                 Uri.fromParts("package", mPackages[0], null));
423         intent.putExtra(Intent.EXTRA_PACKAGES, mPackages);
424         intent.putExtra(Intent.EXTRA_UID, mUid);
425         getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
426                 Activity.RESULT_CANCELED, null, null);
427     }
428     
429     private void reportBatteryUse() {
430         if (mPackages == null) return;
431         
432         ApplicationErrorReport report = new ApplicationErrorReport();
433         report.type = ApplicationErrorReport.TYPE_BATTERY;
434         report.packageName = mPackages[0];
435         report.installerPackageName = mInstaller.getPackageName();
436         report.processName = mPackages[0];
437         report.time = System.currentTimeMillis();
438         report.systemApp = (mApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
439
440         final Bundle args = getArguments();
441         ApplicationErrorReport.BatteryInfo batteryInfo = new ApplicationErrorReport.BatteryInfo();
442         batteryInfo.usagePercent = args.getInt(EXTRA_PERCENT, 1);
443         batteryInfo.durationMicros = args.getLong(EXTRA_USAGE_DURATION, 0);
444         batteryInfo.usageDetails = args.getString(EXTRA_REPORT_DETAILS);
445         batteryInfo.checkinDetails = args.getString(EXTRA_REPORT_CHECKIN_DETAILS);
446         report.batteryInfo = batteryInfo;
447
448         Intent result = new Intent(Intent.ACTION_APP_ERROR);
449         result.setComponent(mInstaller);
450         result.putExtra(Intent.EXTRA_BUG_REPORT, report);
451         result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
452         startActivity(result);
453     }
454     
455     private void fillPackagesSection(int uid) {
456         if (uid < 1) {
457             removePackagesSection();
458             return;
459         }
460         ViewGroup packagesParent = (ViewGroup)mRootView.findViewById(R.id.packages_section);
461         if (packagesParent == null) return;
462         LayoutInflater inflater = getActivity().getLayoutInflater();
463         
464         PackageManager pm = getActivity().getPackageManager();
465         //final Drawable defaultActivityIcon = pm.getDefaultActivityIcon();
466         mPackages = pm.getPackagesForUid(uid);
467         if (mPackages == null || mPackages.length < 2) {
468             removePackagesSection();
469             return;
470         }
471
472         // Convert package names to user-facing labels where possible
473         for (int i = 0; i < mPackages.length; i++) {
474             try {
475                 ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0);
476                 CharSequence label = ai.loadLabel(pm);
477                 //Drawable icon = defaultActivityIcon;
478                 if (label != null) {
479                     mPackages[i] = label.toString();
480                 }
481                 //if (ai.icon != 0) {
482                 //    icon = ai.loadIcon(pm);
483                 //}
484                 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item,
485                         null);
486                 packagesParent.addView(item);
487                 TextView labelView = (TextView) item.findViewById(R.id.label);
488                 labelView.setText(mPackages[i]);
489             } catch (NameNotFoundException e) {
490             }
491         }
492     }
493     
494     private String getDescriptionForDrainType() {
495         return getResources().getString(sDrainTypeDesciptions[mDrainType.ordinal()]);
496     }
497 }