2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.fuelgauge;
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;
48 public class PowerUsageDetail extends Fragment implements Button.OnClickListener {
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
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;
80 public static final int USAGE_SINCE_UNPLUGGED = 1;
81 public static final int USAGE_SINCE_RESET = 2;
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
98 private static final boolean DEBUG = true;
99 private String mTitle;
100 private int mUsageSince;
101 private int[] mTypes;
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
117 private boolean mUsesGps;
119 private static final String TAG = "PowerUsageDetail";
120 private String[] mPackages;
122 ApplicationInfo mApp;
123 ComponentName mInstaller;
126 public void onCreate(Bundle icicle) {
127 super.onCreate(icicle);
131 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
132 View view = mRootView = inflater.inflate(R.layout.power_usage_details, null);
138 public void onResume() {
140 mStartTime = android.os.Process.getElapsedCpuTime();
145 public void onPause() {
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)) {
162 final PackageManager pm = getActivity().getPackageManager();
163 ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo;
165 mAppIcon = ai.loadIcon(pm);
167 } catch (NameNotFoundException nnfe) {
170 } else if (iconId != 0) {
171 mAppIcon = getActivity().getResources().getDrawable(iconId);
173 if (mAppIcon == null) {
174 mAppIcon = getActivity().getPackageManager().getDefaultActivityIcon();
177 // Set the description
178 String summary = getDescriptionForDrainType();
179 ((TextView)mRootView.findViewById(R.id.summary)).setText(summary);
181 mTypes = args.getIntArray(EXTRA_DETAIL_TYPES);
182 mValues = args.getDoubleArray(EXTRA_DETAIL_VALUES);
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));
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);
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);
200 ImageView iconImage = (ImageView)mRootView.findViewById(R.id.icon);
201 iconImage.setImageDrawable(mAppIcon);
203 mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details);
204 mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls);
206 fillDetailsSection();
207 fillPackagesSection(mUid);
208 fillControlsSection(mUid);
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);
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);
222 if (mPackages != null && mPackages.length > 0) {
224 mApp = getActivity().getPackageManager().getApplicationInfo(
226 mInstaller = ApplicationErrorReport.getErrorReportReceiver(
227 getActivity(), mPackages[0], mApp.flags);
228 } catch (NameNotFoundException e) {
231 mReportButton.setEnabled(mInstaller != null);
233 mTwoButtonsPanel.setVisibility(View.GONE);
236 mTwoButtonsPanel.setVisibility(View.GONE);
240 public void onClick(View v) {
241 doAction((Integer) v.getTag());
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]);
250 PreferenceActivity pa = (PreferenceActivity)getActivity();
251 pa.startPreferencePanel(InstalledAppDetails.class.getName(), args,
252 R.string.application_info_label, null, null, 0);
255 private void doAction(int action) {
257 case ACTION_DISPLAY_SETTINGS:
258 startActivity(new Intent(Settings.ACTION_DISPLAY_SETTINGS));
260 case ACTION_WIFI_SETTINGS:
261 startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
263 case ACTION_BLUETOOTH_SETTINGS:
264 startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
266 case ACTION_WIRELESS_SETTINGS:
267 startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
269 case ACTION_APP_DETAILS:
270 startApplicationDetailsActivity();
272 case ACTION_SECURITY_SETTINGS:
273 startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
275 case ACTION_FORCE_STOP:
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]);
293 case R.string.usage_type_data_recv:
294 case R.string.usage_type_data_send:
295 value = Utils.formatBytes(getActivity(), mValues[i]);
297 case R.string.usage_type_no_coverage:
298 value = String.format("%d%%", (int) Math.floor(mValues[i]));
300 case R.string.usage_type_gps:
304 value = Utils.formatElapsedTime(getActivity(), mValues[i]);
306 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text,
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);
317 private void fillControlsSection(int uid) {
318 PackageManager pm = getActivity().getPackageManager();
319 String[] packages = pm.getPackagesForUid(uid);
320 PackageInfo pi = null;
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;
327 boolean removeHeader = true;
328 switch (mDrainType) {
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
339 addControl(R.string.security_settings_title,
340 R.string.battery_sugg_apps_gps, ACTION_SECURITY_SETTINGS);
341 removeHeader = false;
345 addControl(R.string.display_settings,
346 R.string.battery_sugg_display,
347 ACTION_DISPLAY_SETTINGS);
348 removeHeader = false;
351 addControl(R.string.wifi_settings,
352 R.string.battery_sugg_wifi,
353 ACTION_WIFI_SETTINGS);
354 removeHeader = false;
357 addControl(R.string.bluetooth_settings,
358 R.string.battery_sugg_bluetooth_basic,
359 ACTION_BLUETOOTH_SETTINGS);
360 removeHeader = false;
363 if (mNoCoverage > 10) {
364 addControl(R.string.radio_controls_title,
365 R.string.battery_sugg_radio,
366 ACTION_WIRELESS_SETTINGS);
367 removeHeader = false;
372 mControlsParent.setVisibility(View.GONE);
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));
389 private void removePackagesSection() {
391 if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) {
392 view.setVisibility(View.GONE);
394 if ((view = mRootView.findViewById(R.id.packages_section)) != null) {
395 view.setVisibility(View.GONE);
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]);
409 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
411 public void onReceive(Context context, Intent intent) {
412 mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
416 private void checkForceStop() {
417 if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) {
418 mForceStopButton.setEnabled(false);
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);
429 private void reportBatteryUse() {
430 if (mPackages == null) return;
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;
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;
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);
455 private void fillPackagesSection(int uid) {
457 removePackagesSection();
460 ViewGroup packagesParent = (ViewGroup)mRootView.findViewById(R.id.packages_section);
461 if (packagesParent == null) return;
462 LayoutInflater inflater = getActivity().getLayoutInflater();
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();
472 // Convert package names to user-facing labels where possible
473 for (int i = 0; i < mPackages.length; i++) {
475 ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0);
476 CharSequence label = ai.loadLabel(pm);
477 //Drawable icon = defaultActivityIcon;
479 mPackages[i] = label.toString();
481 //if (ai.icon != 0) {
482 // icon = ai.loadIcon(pm);
484 ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_package_item,
486 packagesParent.addView(item);
487 TextView labelView = (TextView) item.findViewById(R.id.label);
488 labelView.setText(mPackages[i]);
489 } catch (NameNotFoundException e) {
494 private String getDescriptionForDrainType() {
495 return getResources().getString(sDrainTypeDesciptions[mDrainType.ordinal()]);