diff options
author | Jason Monk <jmonk@google.com> | 2015-05-04 18:50:19 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-05-04 18:50:19 +0000 |
commit | 0ab13b8326d6f31caff922fd95828254cb95a618 (patch) | |
tree | f514c7a42486c9d599a5fc27891bf32c6999cab6 | |
parent | 32ba436b0b8b955874dd2797a4c317951385fcde (diff) | |
parent | 1eb54eb2ff250eccdd700601011bd6457ddcbec1 (diff) | |
download | packages_apps_Settings-0ab13b8326d6f31caff922fd95828254cb95a618.zip packages_apps_Settings-0ab13b8326d6f31caff922fd95828254cb95a618.tar.gz packages_apps_Settings-0ab13b8326d6f31caff922fd95828254cb95a618.tar.bz2 |
Merge "Add high power whitelist for apps" into mnc-dev
29 files changed, 950 insertions, 595 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index f01110a..6c04af2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -923,6 +923,15 @@ android:value="true" /> </activity> + <activity android:name="Settings$HighPowerApplicationsActivity" + android:label="@string/high_power_apps" + android:taskAffinity=""> + <meta-data android:name="com.android.settings.FRAGMENT_CLASS" + android:value="com.android.settings.applications.ManageApplications" /> + <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED" + android:value="true" /> + </activity> + <!-- Keep compatibility with old shortcuts. --> <activity-alias android:name=".applications.ManageApplications" android:label="@string/applications_settings" diff --git a/res/layout/horizontal_preference.xml b/res/layout/horizontal_preference.xml new file mode 100644 index 0000000..540b7e3 --- /dev/null +++ b/res/layout/horizontal_preference.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:paddingTop="3dp" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"> + + <TextView + android:id="@android:id/title" + android:textAppearance="?android:attr/textAppearanceListItem" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_weight="1" /> + + <TextView + android:id="@android:id/summary" + android:textAppearance="?android:attr/textAppearanceListItemSecondary" + android:textColor="?android:attr/textColorSecondary" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_weight="1" + android:gravity="end" /> + +</LinearLayout> diff --git a/res/layout/power_usage_action_item.xml b/res/layout/power_usage_action_item.xml deleted file mode 100644 index 9e8551e..0000000 --- a/res/layout/power_usage_action_item.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:minHeight="?android:attr/listPreferredItemHeight" - android:gravity="center_vertical" - android:paddingStart="4dip" - android:focusable="true" - android:orientation="vertical" - android:paddingEnd="?android:attr/scrollbarSize"> - - <TextView android:id="@+id/summary" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="2dip" - android:paddingBottom="4dip" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium"/> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <!-- Spacer --> - <View - android:id="@+id/buttons_spacer_left" - android:layout_width="0dip" - android:layout_height="wrap_content" - android:layout_weight="0.7" - android:visibility="invisible" /> - - <Button - android:id="@+id/action_button" - android:layout_width="150dip" - android:paddingEnd="6dip" - android:layout_weight="0.3" - android:layout_height="wrap_content"/> - </LinearLayout> -</LinearLayout> diff --git a/res/layout/power_usage_detail_item_text.xml b/res/layout/power_usage_detail_item_text.xml deleted file mode 100644 index 04b48b8..0000000 --- a/res/layout/power_usage_detail_item_text.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <!--Label for the item--> - <TextView - android:id="@+id/label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textStyle="bold" - android:singleLine="true" - android:ellipsize="middle" - android:layout_alignParentStart="true" - android:layout_toStartOf="@+id/value" - android:layout_marginBottom="4dip" - android:layout_marginTop="4dip" - android:layout_marginEnd="4dip"/> - - <TextView - android:id="@+id/value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentEnd="true" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textStyle="normal" - android:singleLine="true" - android:layout_marginBottom="4dip" - android:layout_marginTop="4dip" /> - -</RelativeLayout> diff --git a/res/layout/power_usage_details.xml b/res/layout/power_usage_details.xml deleted file mode 100644 index f711d9a..0000000 --- a/res/layout/power_usage_details.xml +++ /dev/null @@ -1,111 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<ScrollView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:clipToPadding="false" - android:scrollbarStyle="@integer/preference_scrollbar_style"> - - <LinearLayout - android:id="@+id/all_details" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" - android:paddingTop="5dip" - android:paddingBottom="5dip" - android:orientation="vertical"> - - <include layout="@layout/app_percentage_item" /> - - <!-- Force stop and report buttons --> - <LinearLayout - android:id="@+id/two_buttons_panel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="6dip" - android:orientation="vertical"> - <include - layout="@layout/two_buttons_panel"/> - </LinearLayout> - - <TextView - style="?android:attr/listSeparatorTextViewStyle" - android:text="@string/details_subtitle" /> - - <LinearLayout - android:id="@+id/details" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingStart="6dip" - android:orientation="vertical"> - - <!-- Insert detail items here --> - - </LinearLayout> - - <LinearLayout - android:id="@+id/controls" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <TextView - android:id="@+id/controls_title" - style="?android:attr/listSeparatorTextViewStyle" - android:layout_marginTop="6dip" - android:text="@string/controls_subtitle" /> - - <!-- Controls go here ... --> - - </LinearLayout> - - <LinearLayout - android:id="@+id/messages" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <TextView - android:id="@+id/messages_title" - style="?android:attr/listSeparatorTextViewStyle" - android:layout_marginTop="6dip" /> - - <!-- Messages go here ... --> - - </LinearLayout> - - <TextView - android:id="@+id/packages_section_title" - style="?android:attr/listSeparatorTextViewStyle" - android:layout_marginTop="6dip" - android:text="@string/packages_subtitle" /> - - <LinearLayout - android:id="@+id/packages_section" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingStart="6dip" - android:orientation="vertical"> - - <!-- Insert detail items here --> - - </LinearLayout> - - </LinearLayout> -</ScrollView> diff --git a/res/layout/power_usage_message_item.xml b/res/layout/power_usage_message_item.xml deleted file mode 100644 index 6f9e619..0000000 --- a/res/layout/power_usage_message_item.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<TextView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/message" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="2dip" - android:minHeight="?android:attr/listPreferredItemHeight" - android:paddingStart="4dip" - android:paddingEnd="?android:attr/scrollbarSize" - android:paddingBottom="4dip" - android:ellipsize="marquee" - android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/res/layout/preference_batteryhistory.xml b/res/layout/preference_batteryhistory.xml deleted file mode 100644 index 8697059..0000000 --- a/res/layout/preference_batteryhistory.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:minHeight="@dimen/battery_history_chart_height"> - - <include layout="@layout/battery_history_chart" /> - - <TextView android:id="@+id/labelsHeader" - android:layout_width="match_parent" - android:layout_height="48dp" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textSize="14sp" - android:textColor="?android:attr/colorControlActivated" - android:gravity="start|center_vertical" - android:text="@string/power_usage_list_summary" - android:paddingStart="?android:attr/listPreferredItemPaddingStart" - android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" /> - -</LinearLayout> diff --git a/res/values/strings.xml b/res/values/strings.xml index 3da13e7..7e52036 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6642,4 +6642,31 @@ <!-- Label for process [CHAR LIMIT=25] --> <string name="process_format">Process <xliff:g id="count" example="3">%1$d</xliff:g></string> + <!-- Label for whether app is allowed to use a lot ef power [CHAR LIMIT=25]--> + <string name="high_power" translatable="false">High power</string> + + <!-- List of apps that are allowed to use a lot of power [CHAR LIMIT=25]--> + <string name="high_power_apps" translatable="false">High power apps</string> + + <!-- Summary of app allowed to use a lot of power [CHAR LIMIT=25] --> + <string name="high_power_on">On</string> + + <!-- Summary of app not allowed to use a lot of power [CHAR LIMIT=25] --> + <string name="high_power_off">Off</string> + + <!-- Description of high power switch [CHAR LIMIT=NONE] --> + <string name="high_power_desc" translatable="false">High powered apps description text goes here. This is a placeholder.</string> + + <!-- Description of number of apps with high power turned on [CHAR LIMIT=NONE] --> + <plurals name="high_power_count" translatable="false"> + <item quantity="one">1 app is allowed to ignore restrictions like battery saver mode, sync etc.</item> + <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps is allowed to ignore restrictions like battery saver mode, sync etc.</item> + </plurals> + + <!-- Summary of power usage for an app [CHAR LIMIT=NONE] --> + <string name="battery_summary" translatable="false"><xliff:g id="percentage" example="2">%1$d</xliff:g>%% use since last full charge</string> + + <!-- Summary for app with no battery usage [CHAR LIMIT=NONE] --> + <string name="no_battery_summary" translatable="false">No battery use since last full charge</string> + </resources> diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml index e808210..62431c3 100644 --- a/res/xml/advanced_apps.xml +++ b/res/xml/advanced_apps.xml @@ -26,18 +26,27 @@ settings:keywords="@string/keywords_default_apps" /> <PreferenceScreen + android:key="domain_urls" + android:title="@string/domain_urls_title" + android:fragment="com.android.settings.applications.ManageApplications"> + <extra + android:name="classname" + android:value="com.android.settings.Settings$DomainsURLsAppListActivity" /> + </PreferenceScreen> + + <PreferenceScreen android:key="manage_perms" android:fragment="com.android.settings.applications.ManagePermissions" android:title="@string/app_permissions" settings:keywords="@string/keywords_app_permissions" /> <PreferenceScreen - android:key="domain_urls" - android:title="@string/domain_urls_title" - android:fragment="com.android.settings.applications.ManageApplications"> + android:key="high_power_apps" + android:title="@string/high_power_apps" + android:fragment="com.android.settings.applications.ManageApplications"> <extra - android:name="classname" - android:value="com.android.settings.Settings$DomainsURLsAppListActivity" /> + android:name="classname" + android:value="com.android.settings.Settings$HighPowerApplicationsActivity" /> </PreferenceScreen> </PreferenceScreen> diff --git a/res/layout/power_usage_package_item.xml b/res/xml/high_power_details.xml index 6c31792..81f7806 100644 --- a/res/layout/power_usage_package_item.xml +++ b/res/xml/high_power_details.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2009 The Android Open Source Project +<!-- Copyright (C) 2015 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,13 +14,16 @@ limitations under the License. --> -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/label" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceMedium" - android:singleLine="true" - android:layout_alignParentStart="true" - android:layout_marginBottom="2dip" - android:layout_marginTop="2dip" /> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" + android:title="@string/high_power"> + + <SwitchPreference + android:key="high_power_switch" + android:title="@string/high_power" /> + + <Preference + android:summary="@string/high_power_desc" + android:selectable="false" /> + +</PreferenceScreen> diff --git a/res/xml/installed_app_details.xml b/res/xml/installed_app_details.xml index 233331d..deab13b 100644 --- a/res/xml/installed_app_details.xml +++ b/res/xml/installed_app_details.xml @@ -18,7 +18,8 @@ android:title="@string/application_info_label"> <com.android.settings.applications.LayoutPreference android:key="header_view" - android:layout="@layout/installed_app_details" /> + android:layout="@layout/installed_app_details" + android:selectable="false" /> <Preference android:key="storage_settings" @@ -44,4 +45,10 @@ android:key="preferred_settings" android:title="@string/launch_by_default" android:selectable="true" /> + + <Preference + android:key="battery" + android:title="@string/power_usage_summary_title" + android:selectable="true" /> + </PreferenceScreen> diff --git a/res/xml/power_usage_details.xml b/res/xml/power_usage_details.xml new file mode 100644 index 0000000..ca84e89 --- /dev/null +++ b/res/xml/power_usage_details.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"> + + <com.android.settings.fuelgauge.BatteryHistoryPreference + android:key="battery_history" /> + + <com.android.settings.applications.LayoutPreference + android:key="two_buttons" + android:layout="@layout/two_buttons_panel" + android:selectable="false" /> + + <Preference + android:key="high_power" + android:title="@string/high_power" /> + + <PreferenceCategory + android:key="details_parent" + android:title="@string/details_subtitle" /> + + <PreferenceCategory + android:key="controls_parent" + android:title="@string/controls_subtitle" /> + + <PreferenceCategory + android:key="messages_parent" /> + + <PreferenceCategory + android:key="packages_parent" + android:title="@string/packages_subtitle" /> + +</PreferenceScreen> diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml index 0622513..3f39d47 100644 --- a/res/xml/power_usage_summary.xml +++ b/res/xml/power_usage_summary.xml @@ -17,6 +17,13 @@ <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:settings="http://schemas.android.com/apk/res/com.android.settings" android:title="@string/power_usage_summary_title" - settings:keywords="@string/keywords_battery" - android:key="app_list"> + settings:keywords="@string/keywords_battery"> + + <com.android.settings.fuelgauge.BatteryHistoryPreference + android:key="battery_history" /> + + <PreferenceCategory + android:key="app_list" + android:title="@string/power_usage_list_summary" /> + </PreferenceScreen> diff --git a/src/com/android/settings/AppHeader.java b/src/com/android/settings/AppHeader.java index cd76e80..2f3678b 100644 --- a/src/com/android/settings/AppHeader.java +++ b/src/com/android/settings/AppHeader.java @@ -18,6 +18,7 @@ package com.android.settings; import android.app.Activity; import android.content.Intent; +import android.content.res.ColorStateList; import android.graphics.drawable.Drawable; import android.view.View; import android.view.View.OnClickListener; @@ -29,6 +30,11 @@ public class AppHeader { public static void createAppHeader(final Activity activity, Drawable icon, CharSequence label, final Intent settingsIntent) { + createAppHeader(activity, icon, label, settingsIntent, 0); + } + + public static void createAppHeader(final Activity activity, Drawable icon, CharSequence label, + final Intent settingsIntent, int tintColorRes) { final View content = activity.findViewById(R.id.main_content); final ViewGroup contentParent = (ViewGroup) content.getParent(); final View bar = activity.getLayoutInflater().inflate(R.layout.app_header, @@ -36,6 +42,9 @@ public class AppHeader { final ImageView appIcon = (ImageView) bar.findViewById(R.id.app_icon); appIcon.setImageDrawable(icon); + if (tintColorRes != 0) { + appIcon.setImageTintList(ColorStateList.valueOf(activity.getColor(tintColorRes))); + } final TextView appName = (TextView) bar.findViewById(R.id.app_name); appName.setText(label); diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java index d239d4e..1ee7e73 100644 --- a/src/com/android/settings/InstrumentedFragment.java +++ b/src/com/android/settings/InstrumentedFragment.java @@ -29,6 +29,8 @@ public abstract class InstrumentedFragment extends PreferenceFragment { public static final int VIEW_CATEGORY_DEFAULT_APPS = VIEW_CATEGORY_UNDECLARED + 1; public static final int VIEW_CATEGORY_STORAGE_APPS = VIEW_CATEGORY_UNDECLARED + 2; public static final int VIEW_CATEGORY_USAGE_ACCESS_DETAIL = VIEW_CATEGORY_UNDECLARED + 3; + public static final int VIEW_CATEGORY_HIGH_POWER_DETAILS = VIEW_CATEGORY_UNDECLARED + 4; + public static final int VIEW_CATEGORY_HIGH_POWER_APPS = VIEW_CATEGORY_UNDECLARED + 5; /** * Declare the view of this category. diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java index 6723839..b91275a 100644 --- a/src/com/android/settings/Settings.java +++ b/src/com/android/settings/Settings.java @@ -51,6 +51,7 @@ public class Settings extends SettingsActivity { public static class ApplicationSettingsActivity extends SettingsActivity { /* empty */ } public static class ManageApplicationsActivity extends SettingsActivity { /* empty */ } public static class AllApplicationsActivity extends SettingsActivity { /* empty */ } + public static class HighPowerApplicationsActivity extends SettingsActivity { /* empty */ } public static class AppOpsSummaryActivity extends SettingsActivity { @Override public boolean isValidFragment(String className) { diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index fcbb512..89dbc99 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -89,6 +89,7 @@ import com.android.settings.deviceinfo.PublicVolumeSettings; import com.android.settings.deviceinfo.StorageSettings; import com.android.settings.deviceinfo.UsbSettings; import com.android.settings.fuelgauge.BatterySaverSettings; +import com.android.settings.fuelgauge.PowerUsageDetail; import com.android.settings.fuelgauge.PowerUsageSummary; import com.android.settings.inputmethod.InputMethodAndLanguageSettings; import com.android.settings.inputmethod.KeyboardLayoutPickerFragment; @@ -347,6 +348,7 @@ public class SettingsActivity extends Activity ZenModeEventRuleSettings.class.getName(), ZenModeExternalRuleSettings.class.getName(), ProcessStatsUi.class.getName(), + PowerUsageDetail.class.getName(), }; diff --git a/src/com/android/settings/applications/AdvancedAppSettings.java b/src/com/android/settings/applications/AdvancedAppSettings.java index 52a8b85..5e8d2f4 100644 --- a/src/com/android/settings/applications/AdvancedAppSettings.java +++ b/src/com/android/settings/applications/AdvancedAppSettings.java @@ -26,6 +26,7 @@ import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.applications.ApplicationsState.AppEntry; import com.android.settings.applications.ApplicationsState.Session; +import com.android.settings.fuelgauge.PowerWhitelistBackend; import com.android.settingslib.applications.PermissionsInfo; import java.util.ArrayList; @@ -38,11 +39,13 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements private static final String KEY_APP_PERM = "manage_perms"; private static final String KEY_APP_DOMAIN_URLS = "domain_urls"; + private static final String KEY_HIGH_POWER_APPS = "high_power_apps"; private ApplicationsState mApplicationsState; private Session mSession; private Preference mAppPermsPreference; private Preference mAppDomainURLsPreference; + private Preference mHighPowerPreference; private PermissionsInfo mPermissionsInfo; @Override @@ -55,6 +58,7 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements mAppPermsPreference = findPreference(KEY_APP_PERM); mAppDomainURLsPreference = findPreference(KEY_APP_DOMAIN_URLS); + mHighPowerPreference = findPreference(KEY_HIGH_POWER_APPS); updateUI(); } @@ -70,6 +74,10 @@ public class AdvancedAppSettings extends SettingsPreferenceFragment implements String summary = getResources().getQuantityString( R.plurals.domain_urls_apps_summary, countAppWithDomainURLs, countAppWithDomainURLs); mAppDomainURLsPreference.setSummary(summary); + + int highPowerCount = PowerWhitelistBackend.getInstance().getWhitelistSize(); + mHighPowerPreference.setSummary(getResources().getQuantityString(R.plurals.high_power_count, + highPowerCount, highPowerCount)); } @Override diff --git a/src/com/android/settings/applications/AppInfoBase.java b/src/com/android/settings/applications/AppInfoBase.java index 56fe885..2d402e5 100644 --- a/src/com/android/settings/applications/AppInfoBase.java +++ b/src/com/android/settings/applications/AppInfoBase.java @@ -20,6 +20,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; +import android.app.Fragment; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.Intent; @@ -36,6 +37,7 @@ import android.util.Log; import com.android.settings.InstrumentedPreferenceFragment; import com.android.settings.SettingsActivity; +import com.android.settings.Utils; import com.android.settings.applications.ApplicationsState.AppEntry; import java.util.ArrayList; @@ -194,6 +196,17 @@ public abstract class AppInfoBase extends InstrumentedPreferenceFragment refreshUi(); } + public static void startAppInfoFragment(Class<? extends AppInfoBase> fragment, int titleRes, + String pkg, int uid, Fragment source, int request) { + Bundle args = new Bundle(); + args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg); + + Intent intent = Utils.onBuildStartFragmentIntent(source.getActivity(), fragment.getName(), + args, null, titleRes, null, false); + source.getActivity().startActivityForResultAsUser(intent, request, + new UserHandle(UserHandle.getUserId(uid))); + } + public class MyAlertDialogFragment extends DialogFragment { public MyAlertDialogFragment(int id, int errorCode) { Bundle args = new Bundle(); diff --git a/src/com/android/settings/applications/AppStatePowerBridge.java b/src/com/android/settings/applications/AppStatePowerBridge.java new file mode 100644 index 0000000..40163cb --- /dev/null +++ b/src/com/android/settings/applications/AppStatePowerBridge.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.applications; + +import com.android.settings.applications.ApplicationsState.AppEntry; +import com.android.settings.applications.ApplicationsState.AppFilter; +import com.android.settings.fuelgauge.PowerWhitelistBackend; + +import java.util.ArrayList; + +/** + * Connects data from the PowerWhitelistBackend to ApplicationsState. + */ +public class AppStatePowerBridge extends AppStateBaseBridge { + + private final PowerWhitelistBackend mBackend = PowerWhitelistBackend.getInstance(); + + public AppStatePowerBridge(ApplicationsState appState, Callback callback) { + super(appState, callback); + } + + @Override + protected void loadAllExtraInfo() { + ArrayList<AppEntry> apps = mAppSession.getAllApps(); + final int N = apps.size(); + for (int i = 0; i < N; i++) { + AppEntry app = apps.get(i); + app.extraInfo = mBackend.isWhitelisted(app.info.packageName) + ? Boolean.TRUE : Boolean.FALSE; + } + } + + @Override + protected void updateExtraInfo(AppEntry app, String pkg, int uid) { + app.extraInfo = mBackend.isWhitelisted(pkg) ? Boolean.TRUE : Boolean.FALSE; + } + + public static class HighPowerState { + public boolean isHighPower; + public boolean isSystemHighPower; + } + + public static final AppFilter FILTER_POWER_WHITELISTED = new AppFilter() { + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry info) { + return info.extraInfo == Boolean.TRUE; + } + }; + + public static final AppFilter FILTER_POWER_NOT_WHITELISTED = new AppFilter() { + @Override + public void init() { + } + + @Override + public boolean filterApp(AppEntry info) { + return info.extraInfo == Boolean.FALSE; + } + }; + +} diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index ffd4959..a6fee90 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -38,6 +38,7 @@ import android.net.NetworkTemplate; import android.net.TrafficStats; import android.net.Uri; import android.os.AsyncTask; +import android.os.BatteryStats; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; @@ -57,12 +58,16 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.internal.logging.MetricsLogger; +import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatteryStatsHelper; import com.android.settings.DataUsageSummary; import com.android.settings.DataUsageSummary.AppItem; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.applications.ApplicationsState.AppEntry; +import com.android.settings.fuelgauge.BatteryEntry; +import com.android.settings.fuelgauge.PowerUsageDetail; import com.android.settings.net.ChartData; import com.android.settings.net.ChartDataLoader; import com.android.settings.notification.NotificationBackend; @@ -108,6 +113,7 @@ public class InstalledAppDetails extends AppInfoBase private static final String KEY_PERMISSION = "permission_settings"; private static final String KEY_DATA = "data_settings"; private static final String KEY_LAUNCH = "preferred_settings"; + private static final String KEY_BATTERY = "battery"; private final HashSet<String> mHomePackages = new HashSet<String>(); @@ -131,6 +137,11 @@ public class InstalledAppDetails extends AppInfoBase private ChartData mChartData; private INetworkStatsSession mStatsSession; + private Preference mBatteryPreference; + + private BatteryStatsHelper mBatteryHelper; + private BatterySipper mSipper; + private boolean handleDisableable(Button button) { boolean disableable = false; // Try to prevent the user from bricking their phone @@ -221,6 +232,7 @@ public class InstalledAppDetails extends AppInfoBase } catch (RemoteException e) { throw new RuntimeException(e); } + mBatteryHelper = new BatteryStatsHelper(getActivity(), true); } @Override @@ -236,6 +248,7 @@ public class InstalledAppDetails extends AppInfoBase getLoaderManager().restartLoader(LOADER_CHART_DATA, ChartDataLoader.buildArgs(NetworkTemplate.buildTemplateMobileWildcard(), app), mDataCallbacks); + new BatteryUpdater().execute(); } @Override @@ -263,6 +276,9 @@ public class InstalledAppDetails extends AppInfoBase mPermissionsPreference.setOnPreferenceClickListener(this); mDataPreference = findPreference(KEY_DATA); mDataPreference.setOnPreferenceClickListener(this); + mBatteryPreference = findPreference(KEY_BATTERY); + mBatteryPreference.setEnabled(false); + mBatteryPreference.setOnPreferenceClickListener(this); mLaunchPreference = findPreference(KEY_LAUNCH); if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { @@ -434,6 +450,8 @@ public class InstalledAppDetails extends AppInfoBase mBackend)); mDataPreference.setSummary(getDataSummary()); + updateBattery(); + if (!mInitialized) { // First time init: are we displaying an uninstalled app? mInitialized = true; @@ -459,6 +477,20 @@ public class InstalledAppDetails extends AppInfoBase return true; } + private void updateBattery() { + if (mSipper != null) { + mBatteryPreference.setEnabled(true); + int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( + BatteryStats.STATS_SINCE_CHARGED); + final int percentOfMax = (int) ((mSipper.totalPowerMah) + / mBatteryHelper.getTotalPower() * dischargeAmount + .5f); + mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax)); + } else { + mBatteryPreference.setEnabled(false); + mBatteryPreference.setSummary(getString(R.string.no_battery_summary)); + } + } + private CharSequence getDataSummary() { if (mChartData != null) { long totalBytes = mChartData.detail.getTotalBytes(); @@ -656,6 +688,10 @@ public class InstalledAppDetails extends AppInfoBase SettingsActivity sa = (SettingsActivity) getActivity(); sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1, getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT); + } else if (preference == mBatteryPreference) { + BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); + PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), + mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true); } else { return false; } @@ -700,7 +736,31 @@ public class InstalledAppDetails extends AppInfoBase } } - static class DisableChanger extends AsyncTask<Object, Object, Object> { + private class BatteryUpdater extends AsyncTask<Void, Void, Void> { + @Override + protected Void doInBackground(Void... params) { + mBatteryHelper.create((Bundle) null); + mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, + mUserManager.getUserProfiles()); + List<BatterySipper> usageList = mBatteryHelper.getUsageList(); + final int N = usageList.size(); + for (int i = 0; i < N; i++) { + BatterySipper sipper = usageList.get(i); + if (sipper.getUid() == mPackageInfo.applicationInfo.uid) { + mSipper = sipper; + break; + } + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + refreshUi(); + } + } + + private static class DisableChanger extends AsyncTask<Object, Object, Object> { final PackageManager mPm; final WeakReference<InstalledAppDetails> mActivity; final ApplicationInfo mInfo; diff --git a/src/com/android/settings/applications/LayoutPreference.java b/src/com/android/settings/applications/LayoutPreference.java index c74ad16..8a4e533 100644 --- a/src/com/android/settings/applications/LayoutPreference.java +++ b/src/com/android/settings/applications/LayoutPreference.java @@ -45,7 +45,9 @@ public class LayoutPreference extends Preference { .inflate(layoutResource, null, false); final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); - Utils.forceCustomPadding(allDetails, true /* additive padding */); + if (allDetails != null) { + Utils.forceCustomPadding(allDetails, true /* additive padding */); + } mRootView = view; setShouldDisableView(false); } diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java index b9f49d1..b55cc44 100644 --- a/src/com/android/settings/applications/ManageApplications.java +++ b/src/com/android/settings/applications/ManageApplications.java @@ -55,6 +55,7 @@ import com.android.settings.InstrumentedFragment; import com.android.settings.R; import com.android.settings.Settings.AllApplicationsActivity; import com.android.settings.Settings.DomainsURLsAppListActivity; +import com.android.settings.Settings.HighPowerApplicationsActivity; import com.android.settings.Settings.NotificationAppListActivity; import com.android.settings.Settings.StorageUseActivity; import com.android.settings.Settings.UsageAccessSettingsActivity; @@ -65,6 +66,7 @@ import com.android.settings.applications.ApplicationsState.AppEntry; import com.android.settings.applications.ApplicationsState.AppFilter; import com.android.settings.applications.ApplicationsState.CompoundFilter; import com.android.settings.applications.ApplicationsState.VolumeFilter; +import com.android.settings.fuelgauge.HighPowerDetail; import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend.AppRow; @@ -117,6 +119,8 @@ public class ManageApplications extends InstrumentedFragment public static final int FILTER_APPS_WORK = 8; public static final int FILTER_APPS_WITH_DOMAIN_URLS = 9; public static final int FILTER_APPS_USAGE_ACCESS = 10; + public static final int FILTER_APPS_POWER_WHITELIST = 11; + public static final int FILTER_APPS_POWER_NO_WHITELIST = 12; // This is the string labels for the filter modes above, the order must be kept in sync. public static final int[] FILTER_LABELS = new int[] { @@ -131,6 +135,8 @@ public class ManageApplications extends InstrumentedFragment R.string.filter_work_apps, // Work R.string.filter_with_domain_urls_apps, // Domain URLs R.string.filter_all_apps, // Usage access screen, never displayed + R.string.high_power_on, // High power whitelist, on + R.string.high_power_off, // High power whitelist, off }; // This is the actual mapping to filters from FILTER_ constants above, the order must // be kept in sync. @@ -146,6 +152,8 @@ public class ManageApplications extends InstrumentedFragment ApplicationsState.FILTER_WORK, // Work ApplicationsState.FILTER_WITH_DOMAIN_URLS, // Apps with Domain URLs AppStateUsageBridge.FILTER_APP_USAGE, // Apps with Domain URLs + AppStatePowerBridge.FILTER_POWER_WHITELISTED, // High power whitelist, on + AppStatePowerBridge.FILTER_POWER_NOT_WHITELISTED, // High power whitelist, off }; // sort order @@ -180,11 +188,12 @@ public class ManageApplications extends InstrumentedFragment private Menu mOptionsMenu; - public static final int LIST_TYPE_MAIN = 0; + public static final int LIST_TYPE_MAIN = 0; public static final int LIST_TYPE_NOTIFICATION = 1; public static final int LIST_TYPE_DOMAINS_URLS = 2; - public static final int LIST_TYPE_STORAGE = 3; + public static final int LIST_TYPE_STORAGE = 3; public static final int LIST_TYPE_USAGE_ACCESS = 4; + public static final int LIST_TYPE_HIGH_POWER = 5; private View mRootView; @@ -228,6 +237,8 @@ public class ManageApplications extends InstrumentedFragment } else if (className.equals(UsageAccessSettingsActivity.class.getName())) { mListType = LIST_TYPE_USAGE_ACCESS; getActivity().getActionBar().setTitle(R.string.usage_access_title); + } else if (className.equals(HighPowerApplicationsActivity.class.getName())) { + mListType = LIST_TYPE_HIGH_POWER; } else { mListType = LIST_TYPE_MAIN; } @@ -310,6 +321,9 @@ public class ManageApplications extends InstrumentedFragment mFilterAdapter.enableFilter(FILTER_APPS_SENSITIVE); mFilterAdapter.enableFilter(FILTER_APPS_NO_PEEKING); } + if (mListType == LIST_TYPE_HIGH_POWER) { + mFilterAdapter.enableFilter(FILTER_APPS_POWER_NO_WHITELIST); + } if (mListType == LIST_TYPE_STORAGE) { mApplications.setOverrideFilter(new VolumeFilter(mVolumeUuid)); } @@ -325,12 +339,12 @@ public class ManageApplications extends InstrumentedFragment private int getDefaultFilter() { switch (mListType) { - case LIST_TYPE_MAIN: - return FILTER_APPS_ALL; case LIST_TYPE_DOMAINS_URLS: return FILTER_APPS_WITH_DOMAIN_URLS; case LIST_TYPE_USAGE_ACCESS: return FILTER_APPS_USAGE_ACCESS; + case LIST_TYPE_HIGH_POWER: + return FILTER_APPS_POWER_WHITELIST; default: return FILTER_APPS_ALL; } @@ -349,6 +363,8 @@ public class ManageApplications extends InstrumentedFragment return InstrumentedFragment.VIEW_CATEGORY_STORAGE_APPS; case LIST_TYPE_USAGE_ACCESS: return MetricsLogger.USAGE_ACCESS; + case LIST_TYPE_HIGH_POWER: + return InstrumentedFragment.VIEW_CATEGORY_HIGH_POWER_APPS; default: return MetricsLogger.VIEW_UNKNOWN; } @@ -426,6 +442,9 @@ public class ManageApplications extends InstrumentedFragment case LIST_TYPE_STORAGE: startAppInfoFragment(AppStorageSettings.class, R.string.storage_settings); break; + case LIST_TYPE_HIGH_POWER: + startAppInfoFragment(HighPowerDetail.class, R.string.high_power); + break; // TODO: Figure out if there is a way where we can spin up the profile's settings // process ahead of time, to avoid a long load of data when user clicks on a managed app. // Maybe when they load the list of apps that contains managed profile apps. @@ -436,13 +455,8 @@ public class ManageApplications extends InstrumentedFragment } private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, int titleRes) { - Bundle args = new Bundle(); - args.putString(AppInfoBase.ARG_PACKAGE_NAME, mCurrentPkgName); - - Intent intent = Utils.onBuildStartFragmentIntent(getActivity(), fragment.getName(), args, - null, titleRes, null, false); - getActivity().startActivityForResultAsUser(intent, INSTALLED_APP_DETAILS, - new UserHandle(UserHandle.getUserId(mCurrentUid))); + AppInfoBase.startAppInfoFragment(fragment, titleRes, mCurrentPkgName, mCurrentUid, this, + INSTALLED_APP_DETAILS); } @Override @@ -685,6 +699,8 @@ public class ManageApplications extends InstrumentedFragment mState, this, manageApplications.mNotifBackend); } else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) { mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this); + } else if (mManageApplications.mListType == LIST_TYPE_HIGH_POWER) { + mExtraInfoBridge = new AppStatePowerBridge(mState, this); } else { mExtraInfoBridge = null; } @@ -994,6 +1010,10 @@ public class ManageApplications extends InstrumentedFragment } break; + case LIST_TYPE_HIGH_POWER: + holder.summary.setText(HighPowerDetail.getSummary(mContext, holder.entry)); + break; + default: holder.updateSizeText(mManageApplications.mInvalidSizeStr, mWhichSize); break; diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java index b615ffd..0bf85b5 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java +++ b/src/com/android/settings/fuelgauge/BatteryHistoryPreference.java @@ -19,10 +19,16 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.content.Intent; import android.os.BatteryStats; +import android.os.Bundle; import android.preference.Preference; +import android.preference.PreferenceScreen; +import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; + +import com.android.internal.os.BatteryStatsHelper; import com.android.settings.R; +import com.android.settings.SettingsActivity; /** * Custom preference for displaying power consumption as a bar and an icon on the left for the @@ -31,38 +37,60 @@ import com.android.settings.R; */ public class BatteryHistoryPreference extends Preference { - final private BatteryStats mStats; - final private Intent mBatteryBroadcast; + protected static final String BATTERY_HISTORY_FILE = "tmp_bat_history.bin"; + + private BatteryStats mStats; + private Intent mBatteryBroadcast; - private boolean mHideLabels; - private View mLabelHeader; private BatteryHistoryChart mChart; + private BatteryStatsHelper mHelper; - public BatteryHistoryPreference(Context context, BatteryStats stats, Intent batteryBroadcast) { - super(context); - setLayoutResource(R.layout.preference_batteryhistory); - mStats = stats; - mBatteryBroadcast = batteryBroadcast; + public BatteryHistoryPreference(Context context, AttributeSet attrs) { + super(context, attrs); } - BatteryStats getStats() { - return mStats; + @Override + public void performClick(PreferenceScreen preferenceScreen) { + if (!isEnabled()) { + return; + } + mHelper.storeStatsHistoryInFile(BATTERY_HISTORY_FILE); + Bundle args = new Bundle(); + args.putString(BatteryHistoryDetail.EXTRA_STATS, BATTERY_HISTORY_FILE); + args.putParcelable(BatteryHistoryDetail.EXTRA_BROADCAST, + mHelper.getBatteryBroadcast()); + if (getContext() instanceof SettingsActivity) { + SettingsActivity sa = (SettingsActivity) getContext(); + sa.startPreferencePanel(BatteryHistoryDetail.class.getName(), args, + R.string.history_details_title, null, null, 0); + } } - public void setHideLabels(boolean hide) { - if (mHideLabels != hide) { - mHideLabels = hide; - if (mLabelHeader != null) { - mLabelHeader.setVisibility(hide ? View.GONE : View.VISIBLE); - } + public void setStats(BatteryStatsHelper batteryStats) { + // Clear out the chart to receive new data. + mChart = null; + mHelper = batteryStats; + mStats = batteryStats.getStats(); + mBatteryBroadcast = batteryStats.getBatteryBroadcast(); + if (getLayoutResource() != R.layout.battery_history_chart) { + // Now we should have some data, set the layout we want. + setLayoutResource(R.layout.battery_history_chart); } + notifyChanged(); + } + + BatteryStats getStats() { + return mStats; } @Override protected void onBindView(View view) { super.onBindView(view); - BatteryHistoryChart chart = (BatteryHistoryChart)view.findViewById( + if (mStats == null) { + return; + } + BatteryHistoryChart chart = (BatteryHistoryChart) view.findViewById( R.id.battery_history_chart); if (mChart == null) { // First time: use and initialize this chart. @@ -71,15 +99,13 @@ public class BatteryHistoryPreference extends Preference { } else { // All future times: forget the newly inflated chart, re-use the // already initialized chart from last time. - ViewGroup parent = (ViewGroup)chart.getParent(); + ViewGroup parent = (ViewGroup) chart.getParent(); int index = parent.indexOfChild(chart); parent.removeViewAt(index); if (mChart.getParent() != null) { - ((ViewGroup)mChart.getParent()).removeView(mChart); + ((ViewGroup) mChart.getParent()).removeView(mChart); } parent.addView(mChart, index); } - mLabelHeader = view.findViewById(R.id.labelsHeader); - mLabelHeader.setVisibility(mHideLabels ? View.GONE : View.VISIBLE); } } diff --git a/src/com/android/settings/fuelgauge/HighPowerDetail.java b/src/com/android/settings/fuelgauge/HighPowerDetail.java new file mode 100644 index 0000000..d82cb6f --- /dev/null +++ b/src/com/android/settings/fuelgauge/HighPowerDetail.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import android.app.AlertDialog; +import android.content.Context; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.SwitchPreference; + +import com.android.settings.InstrumentedFragment; +import com.android.settings.R; +import com.android.settings.applications.AppInfoWithHeader; +import com.android.settings.applications.ApplicationsState.AppEntry; + +public class HighPowerDetail extends AppInfoWithHeader implements OnPreferenceChangeListener { + + private static final String KEY_HIGH_POWER_SWITCH = "high_power_switch"; + + private final PowerWhitelistBackend mBackend = PowerWhitelistBackend.getInstance(); + + private SwitchPreference mUsageSwitch; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.high_power_details); + mUsageSwitch = (SwitchPreference) findPreference(KEY_HIGH_POWER_SWITCH); + mUsageSwitch.setOnPreferenceChangeListener(this); + } + + @Override + protected boolean refreshUi() { + mUsageSwitch.setEnabled(!mBackend.isSysWhitelisted(mPackageName)); + mUsageSwitch.setChecked(mBackend.isWhitelisted(mPackageName)); + return true; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (newValue == Boolean.TRUE) { + mBackend.addApp(mPackageName); + } else { + mBackend.removeApp(mPackageName); + } + return true; + } + + @Override + protected AlertDialog createDialog(int id, int errorCode) { + return null; + } + + @Override + protected int getMetricsCategory() { + return InstrumentedFragment.VIEW_CATEGORY_HIGH_POWER_DETAILS; + } + + public static CharSequence getSummary(Context context, AppEntry entry) { + return getSummary(context, entry.info.packageName); + } + + public static CharSequence getSummary(Context context, String pkg) { + return context.getString(PowerWhitelistBackend.getInstance().isWhitelisted(pkg) + ? R.string.high_power_on : R.string.high_power_off); + } + +} diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java new file mode 100644 index 0000000..269249a --- /dev/null +++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.fuelgauge; + +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.BatteryStats; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.UserManager; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import com.android.internal.os.BatteryStatsHelper; +import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; + +/** + * Common base class for things that need to show the battery usage graph. + */ +public abstract class PowerUsageBase extends SettingsPreferenceFragment { + + // +1 to allow ordering for PowerUsageSummary. + private static final int MENU_STATS_REFRESH = Menu.FIRST + 1; + + protected BatteryStatsHelper mStatsHelper; + protected UserManager mUm; + + private String mBatteryLevel; + private String mBatteryStatus; + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mStatsHelper = new BatteryStatsHelper(activity, true); + } + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + mStatsHelper.create(icicle); + setHasOptionsMenu(true); + } + + @Override + public void onStart() { + super.onStart(); + mStatsHelper.clearStats(); + } + + @Override + public void onResume() { + super.onResume(); + BatteryStatsHelper.dropFile(getActivity(), BatteryHistoryPreference.BATTERY_HISTORY_FILE); + updateBatteryStatus(getActivity().registerReceiver(mBatteryInfoReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED))); + if (mHandler.hasMessages(MSG_REFRESH_STATS)) { + mHandler.removeMessages(MSG_REFRESH_STATS); + mStatsHelper.clearStats(); + } + } + + @Override + public void onPause() { + super.onPause(); + getActivity().unregisterReceiver(mBatteryInfoReceiver); + } + + @Override + public void onStop() { + super.onStop(); + mHandler.removeMessages(MSG_REFRESH_STATS); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (getActivity().isChangingConfigurations()) { + mStatsHelper.storeState(); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh) + .setIcon(com.android.internal.R.drawable.ic_menu_refresh) + .setAlphabeticShortcut('r'); + refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | + MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case MENU_STATS_REFRESH: + mStatsHelper.clearStats(); + refreshStats(); + mHandler.removeMessages(MSG_REFRESH_STATS); + return true; + } + return super.onOptionsItemSelected(item); + } + + protected void refreshStats() { + mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, mUm.getUserProfiles()); + } + + protected void updatePreference(BatteryHistoryPreference historyPref) { + historyPref.setStats(mStatsHelper); + } + + private boolean updateBatteryStatus(Intent intent) { + if (intent != null) { + String batteryLevel = com.android.settings.Utils.getBatteryPercentage(intent); + String batteryStatus = com.android.settings.Utils.getBatteryStatus(getResources(), + intent); + if (!batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals(mBatteryStatus)) { + mBatteryLevel = batteryLevel; + mBatteryStatus = batteryStatus; + return true; + } + } + return false; + } + + static final int MSG_REFRESH_STATS = 100; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_REFRESH_STATS: + mStatsHelper.clearStats(); + refreshStats(); + break; + } + } + }; + + private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_BATTERY_CHANGED.equals(action) + && updateBatteryStatus(intent)) { + if (!mHandler.hasMessages(MSG_REFRESH_STATS)) { + mHandler.sendEmptyMessageDelayed(MSG_REFRESH_STATS, 500); + } + } + } + }; + +} diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 1f13e8e..4aa935a 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java @@ -16,8 +16,6 @@ package com.android.settings.fuelgauge; -import static com.android.settings.Utils.prepareCustomPreferencesList; - import android.app.Activity; import android.app.ActivityManager; import android.app.ApplicationErrorReport; @@ -30,33 +28,34 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.BatteryStats; import android.os.Bundle; import android.os.Process; import android.os.UserHandle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceCategory; import android.text.TextUtils; -import android.view.LayoutInflater; +import android.util.Log; import android.view.View; -import android.view.ViewGroup; import android.widget.Button; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BatterySipper; +import com.android.internal.os.BatterySipper.DrainType; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.util.FastPrintWriter; +import com.android.settings.AppHeader; import com.android.settings.DisplaySettings; -import com.android.settings.InstrumentedFragment; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; import com.android.settings.WirelessSettings; +import com.android.settings.applications.AppInfoBase; import com.android.settings.applications.InstalledAppDetails; +import com.android.settings.applications.LayoutPreference; import com.android.settings.bluetooth.BluetoothSettings; import com.android.settings.location.LocationSettings; import com.android.settings.wifi.WifiSettings; @@ -65,7 +64,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; -public class PowerUsageDetail extends InstrumentedFragment implements Button.OnClickListener { +public class PowerUsageDetail extends PowerUsageBase implements Button.OnClickListener { // Note: Must match the sequence of the DrainType private static int[] sDrainTypeDesciptions = new int[] { @@ -292,51 +291,57 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC public static final String EXTRA_ICON_ID = "iconId"; // Int public static final String EXTRA_SHOW_LOCATION_BUTTON = "showLocationButton"; // Boolean + private static final String TAG = "PowerUsageDetail"; + + private static final String KEY_DETAILS_PARENT = "details_parent"; + private static final String KEY_CONTROLS_PARENT = "controls_parent"; + private static final String KEY_MESSAGES_PARENT = "messages_parent"; + private static final String KEY_PACKAGES_PARENT = "packages_parent"; + private static final String KEY_BATTERY_HISTORY = "battery_history"; + private static final String KEY_TWO_BUTTONS = "two_buttons"; + private static final String KEY_HIGH_POWER = "high_power"; + private PackageManager mPm; private DevicePolicyManager mDpm; - private String mTitle; private int mUsageSince; private int[] mTypes; private int mUid; private double[] mValues; - private View mRootView; - private TextView mTitleView; - private ViewGroup mTwoButtonsPanel; private Button mForceStopButton; private Button mReportButton; - private ViewGroup mDetailsParent; - private ViewGroup mControlsParent; - private ViewGroup mMessagesParent; private long mStartTime; private BatterySipper.DrainType mDrainType; - private Drawable mAppIcon; private double mNoCoverage; // Percentage of time that there was no coverage + private BatteryHistoryPreference mHistPref; + private PreferenceCategory mDetailsParent; + private PreferenceCategory mControlsParent; + private PreferenceCategory mMessagesParent; + private PreferenceCategory mPackagesParent; + private boolean mUsesGps; private boolean mShowLocationButton; - private static final String TAG = "PowerUsageDetail"; private String[] mPackages; ApplicationInfo mApp; ComponentName mInstaller; + private Preference mHighPower; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); mPm = getActivity().getPackageManager(); mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); - } - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.power_usage_details, container, false); - prepareCustomPreferencesList(container, view, view, false); + addPreferencesFromResource(R.xml.power_usage_details); + mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_HISTORY); + mDetailsParent = (PreferenceCategory) findPreference(KEY_DETAILS_PARENT); + mControlsParent = (PreferenceCategory) findPreference(KEY_CONTROLS_PARENT); + mMessagesParent = (PreferenceCategory) findPreference(KEY_MESSAGES_PARENT); + mPackagesParent = (PreferenceCategory) findPreference(KEY_PACKAGES_PARENT); - mRootView = view; createDetails(); - return view; } @Override @@ -349,76 +354,31 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC super.onResume(); mStartTime = android.os.Process.getElapsedCpuTime(); checkForceStop(); - } - - @Override - public void onPause() { - super.onPause(); + if (mHighPower != null) { + mHighPower.setSummary(HighPowerDetail.getSummary(getActivity(), mApp.packageName)); + } } private void createDetails() { final Bundle args = getArguments(); - mTitle = args.getString(EXTRA_TITLE); - final int percentage = args.getInt(EXTRA_PERCENT, 1); - final int gaugeValue = args.getInt(EXTRA_GAUGE, 1); + Context context = getActivity(); mUsageSince = args.getInt(EXTRA_USAGE_SINCE, USAGE_SINCE_UNPLUGGED); mUid = args.getInt(EXTRA_UID, 0); + mPackages = context.getPackageManager().getPackagesForUid(mUid); mDrainType = (BatterySipper.DrainType) args.getSerializable(EXTRA_DRAIN_TYPE); mNoCoverage = args.getDouble(EXTRA_NO_COVERAGE, 0); - String iconPackage = args.getString(EXTRA_ICON_PACKAGE); - int iconId = args.getInt(EXTRA_ICON_ID, 0); mShowLocationButton = args.getBoolean(EXTRA_SHOW_LOCATION_BUTTON); - if (!TextUtils.isEmpty(iconPackage)) { - try { - final PackageManager pm = getActivity().getPackageManager(); - ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo; - if (ai != null) { - mAppIcon = ai.loadIcon(pm); - } - } catch (NameNotFoundException nnfe) { - // Use default icon - } - } else if (iconId != 0) { - mAppIcon = getActivity().getDrawable(iconId); - } - if (mAppIcon == null) { - mAppIcon = getActivity().getPackageManager().getDefaultActivityIcon(); - } - // Set the description - final TextView summary = (TextView) mRootView.findViewById(android.R.id.summary); - summary.setText(getDescriptionForDrainType()); - summary.setVisibility(View.VISIBLE); + setupHeader(); mTypes = args.getIntArray(EXTRA_DETAIL_TYPES); mValues = args.getDoubleArray(EXTRA_DETAIL_VALUES); - mTitleView = (TextView) mRootView.findViewById(android.R.id.title); - mTitleView.setText(mTitle); - - final TextView text1 = (TextView)mRootView.findViewById(android.R.id.text1); - text1.setText(Utils.formatPercentage(percentage)); - - mTwoButtonsPanel = (ViewGroup)mRootView.findViewById(R.id.two_buttons_panel); - mForceStopButton = (Button)mRootView.findViewById(R.id.left_button); - mReportButton = (Button)mRootView.findViewById(R.id.right_button); + LayoutPreference twoButtons = (LayoutPreference) findPreference(KEY_TWO_BUTTONS); + mForceStopButton = (Button) twoButtons.findViewById(R.id.left_button); + mReportButton = (Button) twoButtons.findViewById(R.id.right_button); mForceStopButton.setEnabled(false); - final ProgressBar progress = (ProgressBar) mRootView.findViewById(android.R.id.progress); - progress.setProgress(gaugeValue); - - final ImageView icon = (ImageView) mRootView.findViewById(android.R.id.icon); - icon.setImageDrawable(mAppIcon); - - mDetailsParent = (ViewGroup)mRootView.findViewById(R.id.details); - mControlsParent = (ViewGroup)mRootView.findViewById(R.id.controls); - mMessagesParent = (ViewGroup)mRootView.findViewById(R.id.messages); - - fillDetailsSection(); - fillPackagesSection(mUid); - fillControlsSection(mUid); - fillMessagesSection(mUid); - if (mUid >= Process.FIRST_APPLICATION_UID) { mForceStopButton.setText(R.string.force_stop); mForceStopButton.setTag(ACTION_FORCE_STOP); @@ -426,27 +386,86 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC mReportButton.setText(com.android.internal.R.string.report); mReportButton.setTag(ACTION_REPORT); mReportButton.setOnClickListener(this); - + + if (mPackages != null && mPackages.length > 0) { + try { + mApp = context.getPackageManager().getApplicationInfo( + mPackages[0], 0); + } catch (NameNotFoundException e) { + } + } else { + Log.d(TAG, "No packages!!"); + } // check if error reporting is enabled in secure settings - int enabled = android.provider.Settings.Global.getInt(getActivity().getContentResolver(), + int enabled = android.provider.Settings.Global.getInt(context.getContentResolver(), android.provider.Settings.Global.SEND_ACTION_APP_ERROR, 0); if (enabled != 0) { - if (mPackages != null && mPackages.length > 0) { - try { - mApp = getActivity().getPackageManager().getApplicationInfo( - mPackages[0], 0); - mInstaller = ApplicationErrorReport.getErrorReportReceiver( - getActivity(), mPackages[0], mApp.flags); - } catch (NameNotFoundException e) { - } + if (mApp != null) { + mInstaller = ApplicationErrorReport.getErrorReportReceiver( + context, mPackages[0], mApp.flags); } mReportButton.setEnabled(mInstaller != null); } else { - mTwoButtonsPanel.setVisibility(View.GONE); + removePreference(KEY_TWO_BUTTONS); + } + if (mApp != null) { + mHighPower = findPreference(KEY_HIGH_POWER); + mHighPower.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + AppInfoBase.startAppInfoFragment(HighPowerDetail.class, R.string.high_power, + mApp.packageName, mApp.uid, PowerUsageDetail.this, 0); + return true; + } + }); + } else { + removePreference(KEY_HIGH_POWER); } } else { - mTwoButtonsPanel.setVisibility(View.GONE); + removePreference(KEY_TWO_BUTTONS); + removePreference(KEY_HIGH_POWER); } + + refreshStats(); + + fillDetailsSection(); + fillPackagesSection(mUid); + fillControlsSection(mUid); + fillMessagesSection(mUid); + } + + @Override + protected void refreshStats() { + super.refreshStats(); + updatePreference(mHistPref); + } + + private void setupHeader() { + final Bundle args = getArguments(); + String title = args.getString(EXTRA_TITLE); + String iconPackage = args.getString(EXTRA_ICON_PACKAGE); + int iconId = args.getInt(EXTRA_ICON_ID, 0); + Drawable appIcon = null; + + if (!TextUtils.isEmpty(iconPackage)) { + try { + final PackageManager pm = getActivity().getPackageManager(); + ApplicationInfo ai = pm.getPackageInfo(iconPackage, 0).applicationInfo; + if (ai != null) { + appIcon = ai.loadIcon(pm); + } + } catch (NameNotFoundException nnfe) { + // Use default icon + } + } else if (iconId != 0) { + appIcon = getActivity().getDrawable(iconId); + } + if (appIcon == null) { + appIcon = getActivity().getPackageManager().getDefaultActivityIcon(); + } + + AppHeader.createAppHeader(getActivity(), appIcon, title, null, + mDrainType != DrainType.APP ? android.R.color.white : 0); } public void onClick(View v) { @@ -500,7 +519,6 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC } private void fillDetailsSection() { - LayoutInflater inflater = getActivity().getLayoutInflater(); if (mTypes != null && mValues != null) { for (int i = 0; i < mTypes.length; i++) { // Only add an item if the time is greater than zero @@ -530,17 +548,21 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC default: value = Utils.formatElapsedTime(getActivity(), mValues[i], true); } - ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_detail_item_text, - null); - mDetailsParent.addView(item); - TextView labelView = (TextView) item.findViewById(R.id.label); - TextView valueView = (TextView) item.findViewById(R.id.value); - labelView.setText(label); - valueView.setText(value); + addHorizontalPreference(mDetailsParent, label, value); } } } + private void addHorizontalPreference(PreferenceCategory parent, CharSequence title, + CharSequence summary) { + Preference pref = new Preference(getActivity()); + pref.setLayoutResource(R.layout.horizontal_preference); + pref.setTitle(title); + pref.setSummary(summary); + pref.setSelectable(false); + parent.addPreference(pref); + } + private void fillControlsSection(int uid) { PackageManager pm = getActivity().getPackageManager(); String[] packages = pm.getPackagesForUid(uid); @@ -597,21 +619,22 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC break; } if (removeHeader) { - mControlsParent.setVisibility(View.GONE); + mControlsParent.setTitle(null); } } - private void addControl(int title, int summary, int action) { - final Resources res = getResources(); - LayoutInflater inflater = getActivity().getLayoutInflater(); - ViewGroup item = (ViewGroup) inflater.inflate(R.layout.power_usage_action_item,null); - mControlsParent.addView(item); - Button actionButton = (Button) item.findViewById(R.id.action_button); - TextView summaryView = (TextView) item.findViewById(R.id.summary); - actionButton.setText(res.getString(title)); - summaryView.setText(res.getString(summary)); - actionButton.setOnClickListener(this); - actionButton.setTag(new Integer(action)); + private void addControl(int pageSummary, int actionTitle, final int action) { + Preference pref = new Preference(getActivity()); + pref.setTitle(actionTitle); + pref.setSummary(pageSummary); + pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + doAction(action); + return true; + } + }); + mControlsParent.addPreference(pref); } private void fillMessagesSection(int uid) { @@ -623,27 +646,16 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC break; } if (removeHeader) { - mMessagesParent.setVisibility(View.GONE); + mMessagesParent.setTitle(null); } } private void addMessage(int message) { - final Resources res = getResources(); - LayoutInflater inflater = getActivity().getLayoutInflater(); - View item = inflater.inflate(R.layout.power_usage_message_item, null); - mMessagesParent.addView(item); - TextView messageView = (TextView) item.findViewById(R.id.message); - messageView.setText(res.getText(message)); + addHorizontalPreference(mMessagesParent, getString(message), null); } private void removePackagesSection() { - View view; - if ((view = mRootView.findViewById(R.id.packages_section_title)) != null) { - view.setVisibility(View.GONE); - } - if ((view = mRootView.findViewById(R.id.packages_section)) != null) { - view.setVisibility(View.GONE); - } + getPreferenceScreen().removePreference(mPackagesParent); } private void killProcesses() { @@ -663,7 +675,7 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED); } }; - + private void checkForceStop() { if (mPackages == null || mUid < Process.FIRST_APPLICATION_UID) { mForceStopButton.setEnabled(false); @@ -693,10 +705,10 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); } - + private void reportBatteryUse() { if (mPackages == null) return; - + ApplicationErrorReport report = new ApplicationErrorReport(); report.type = ApplicationErrorReport.TYPE_BATTERY; report.packageName = mPackages[0]; @@ -719,46 +731,29 @@ public class PowerUsageDetail extends InstrumentedFragment implements Button.OnC result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(result); } - + private void fillPackagesSection(int uid) { if (uid < 1) { removePackagesSection(); return; } - ViewGroup packagesParent = (ViewGroup)mRootView.findViewById(R.id.packages_section); - if (packagesParent == null) return; - LayoutInflater inflater = getActivity().getLayoutInflater(); - - PackageManager pm = getActivity().getPackageManager(); - //final Drawable defaultActivityIcon = pm.getDefaultActivityIcon(); - mPackages = pm.getPackagesForUid(uid); if (mPackages == null || mPackages.length < 2) { removePackagesSection(); return; } + PackageManager pm = getPackageManager(); // Convert package names to user-facing labels where possible for (int i = 0; i < mPackages.length; i++) { try { ApplicationInfo ai = pm.getApplicationInfo(mPackages[i], 0); CharSequence label = ai.loadLabel(pm); - //Drawable icon = defaultActivityIcon; if (label != null) { mPackages[i] = label.toString(); } - //if (ai.icon != 0) { - // icon = ai.loadIcon(pm); - //} - View item = inflater.inflate(R.layout.power_usage_package_item, null); - packagesParent.addView(item); - TextView labelView = (TextView) item.findViewById(R.id.label); - labelView.setText(mPackages[i]); + addHorizontalPreference(mPackagesParent, mPackages[i], null); } catch (NameNotFoundException e) { } } } - - private String getDescriptionForDrainType() { - return getResources().getString(sDrainTypeDesciptions[mDrainType.ordinal()]); - } } diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 297fe1a..89a3325 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -17,10 +17,6 @@ package com.android.settings.fuelgauge; import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.graphics.drawable.Drawable; import android.os.BatteryStats; import android.os.Build; @@ -28,7 +24,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.UserHandle; -import android.os.UserManager; import android.preference.Preference; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; @@ -39,12 +34,12 @@ import android.view.MenuItem; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BatterySipper; -import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.PowerProfile; import com.android.settings.HelpUtils; -import com.android.settings.InstrumentedPreferenceFragment; import com.android.settings.R; +import com.android.settings.Settings.HighPowerApplicationsActivity; import com.android.settings.SettingsActivity; +import com.android.settings.applications.ManageApplications; import java.util.List; @@ -52,27 +47,22 @@ import java.util.List; * Displays a list of apps and subsystems that consume power, ordered by how much power was * consumed since the last time it was unplugged. */ -public class PowerUsageSummary extends InstrumentedPreferenceFragment { +public class PowerUsageSummary extends PowerUsageBase { private static final boolean DEBUG = false; static final String TAG = "PowerUsageSummary"; private static final String KEY_APP_LIST = "app_list"; - - private static final String BATTERY_HISTORY_FILE = "tmp_bat_history.bin"; + private static final String KEY_BATTERY_HISTORY = "battery_history"; private static final int MENU_STATS_TYPE = Menu.FIRST; - private static final int MENU_STATS_REFRESH = Menu.FIRST + 1; private static final int MENU_BATTERY_SAVER = Menu.FIRST + 2; - private static final int MENU_HELP = Menu.FIRST + 3; - - private UserManager mUm; + private static final int MENU_HIGH_POWER_APPS = Menu.FIRST + 3; + private static final int MENU_HELP = Menu.FIRST + 4; private BatteryHistoryPreference mHistPref; private PreferenceGroup mAppListGroup; - private String mBatteryLevel; - private String mBatteryStatus; private int mStatsType = BatteryStats.STATS_SINCE_CHARGED; @@ -81,43 +71,13 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10; private static final int SECONDS_IN_HOUR = 60 * 60; - private BatteryStatsHelper mStatsHelper; - - private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() { - - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_BATTERY_CHANGED.equals(action) - && updateBatteryStatus(intent)) { - if (!mHandler.hasMessages(MSG_REFRESH_STATS)) { - mHandler.sendEmptyMessageDelayed(MSG_REFRESH_STATS, 500); - } - } - } - }; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); - mStatsHelper = new BatteryStatsHelper(activity, true); - } - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); - mStatsHelper.create(icicle); addPreferencesFromResource(R.xml.power_usage_summary); + mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_HISTORY); mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); - setHasOptionsMenu(true); - } - - @Override - public void onStart() { - super.onStart(); - mStatsHelper.clearStats(); } @Override @@ -128,13 +88,6 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { @Override public void onResume() { super.onResume(); - BatteryStatsHelper.dropFile(getActivity(), BATTERY_HISTORY_FILE); - updateBatteryStatus(getActivity().registerReceiver(mBatteryInfoReceiver, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED))); - if (mHandler.hasMessages(MSG_REFRESH_STATS)) { - mHandler.removeMessages(MSG_REFRESH_STATS); - mStatsHelper.clearStats(); - } refreshStats(); } @@ -142,38 +95,19 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { public void onPause() { BatteryEntry.stopRequestQueue(); mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON); - getActivity().unregisterReceiver(mBatteryInfoReceiver); super.onPause(); } @Override - public void onStop() { - super.onStop(); - mHandler.removeMessages(MSG_REFRESH_STATS); - } - - @Override public void onDestroy() { super.onDestroy(); if (getActivity().isChangingConfigurations()) { - mStatsHelper.storeState(); BatteryEntry.clearUidCache(); } } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { - if (preference instanceof BatteryHistoryPreference) { - mStatsHelper.storeStatsHistoryInFile(BATTERY_HISTORY_FILE); - Bundle args = new Bundle(); - args.putString(BatteryHistoryDetail.EXTRA_STATS, BATTERY_HISTORY_FILE); - args.putParcelable(BatteryHistoryDetail.EXTRA_BROADCAST, - mStatsHelper.getBatteryBroadcast()); - SettingsActivity sa = (SettingsActivity) getActivity(); - sa.startPreferencePanel(BatteryHistoryDetail.class.getName(), args, - R.string.history_details_title, null, null, 0); - return super.onPreferenceTreeClick(preferenceScreen, preference); - } if (!(preference instanceof PowerGaugePreference)) { return false; } @@ -186,20 +120,18 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); if (DEBUG) { menu.add(0, MENU_STATS_TYPE, 0, R.string.menu_stats_total) .setIcon(com.android.internal.R.drawable.ic_menu_info_details) .setAlphabeticShortcut('t'); } - MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh) - .setIcon(com.android.internal.R.drawable.ic_menu_refresh) - .setAlphabeticShortcut('r'); - refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | - MenuItem.SHOW_AS_ACTION_WITH_TEXT); MenuItem batterySaver = menu.add(0, MENU_BATTERY_SAVER, 0, R.string.battery_saver); batterySaver.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); + menu.add(0, MENU_HIGH_POWER_APPS, 0, R.string.high_power_apps); + String helpUrl; if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_battery))) { final MenuItem help = menu.add(0, MENU_HELP, 0, R.string.help_label); @@ -209,6 +141,7 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { + final SettingsActivity sa = (SettingsActivity) getActivity(); switch (item.getItemId()) { case MENU_STATS_TYPE: if (mStatsType == BatteryStats.STATS_SINCE_CHARGED) { @@ -218,59 +151,40 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { } refreshStats(); return true; - case MENU_STATS_REFRESH: - mStatsHelper.clearStats(); - refreshStats(); - mHandler.removeMessages(MSG_REFRESH_STATS); - return true; case MENU_BATTERY_SAVER: - final SettingsActivity sa = (SettingsActivity) getActivity(); sa.startPreferencePanel(BatterySaverSettings.class.getName(), null, R.string.battery_saver, null, null, 0); return true; + case MENU_HIGH_POWER_APPS: + Bundle args = new Bundle(); + args.putString(ManageApplications.EXTRA_CLASSNAME, + HighPowerApplicationsActivity.class.getName()); + sa.startPreferencePanel(ManageApplications.class.getName(), args, + R.string.high_power_apps, null, null, 0); + return true; default: - return false; + return super.onOptionsItemSelected(item); } } private void addNotAvailableMessage() { Preference notAvailable = new Preference(getActivity()); notAvailable.setTitle(R.string.power_usage_not_available); - mHistPref.setHideLabels(true); mAppListGroup.addPreference(notAvailable); } - private boolean updateBatteryStatus(Intent intent) { - if (intent != null) { - String batteryLevel = com.android.settings.Utils.getBatteryPercentage(intent); - String batteryStatus = com.android.settings.Utils.getBatteryStatus(getResources(), - intent); - if (!batteryLevel.equals(mBatteryLevel) || !batteryStatus.equals(mBatteryStatus)) { - mBatteryLevel = batteryLevel; - mBatteryStatus = batteryStatus; - return true; - } - } - return false; - } - - private void refreshStats() { + protected void refreshStats() { + super.refreshStats(); + updatePreference(mHistPref); mAppListGroup.removeAll(); mAppListGroup.setOrderingAsAdded(false); - mHistPref = new BatteryHistoryPreference(getActivity(), mStatsHelper.getStats(), - mStatsHelper.getBatteryBroadcast()); - mHistPref.setOrder(-1); - mAppListGroup.addPreference(mHistPref); boolean addedSome = false; final PowerProfile powerProfile = mStatsHelper.getPowerProfile(); final BatteryStats stats = mStatsHelper.getStats(); final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL); - if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) { - final List<UserHandle> profiles = mUm.getUserProfiles(); - - mStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, profiles); + if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP) { final List<BatterySipper> usageList = mStatsHelper.getUsageList(); final int dischargeAmount = stats != null ? stats.getDischargeAmount(mStatsType) : 0; @@ -320,7 +234,8 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { final PowerGaugePreference pref = new PowerGaugePreference(getActivity(), badgedIcon, contentDescription, entry); - final double percentOfMax = (sipper.totalPowerMah * 100) / mStatsHelper.getMaxPower(); + final double percentOfMax = (sipper.totalPowerMah * 100) + / mStatsHelper.getMaxPower(); sipper.percent = percentOfTotal; pref.setTitle(entry.getLabel()); pref.setOrder(i + 1); @@ -342,8 +257,6 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { BatteryEntry.startRequestQueue(); } - static final int MSG_REFRESH_STATS = 100; - Handler mHandler = new Handler() { @Override @@ -367,9 +280,6 @@ public class PowerUsageSummary extends InstrumentedPreferenceFragment { activity.reportFullyDrawn(); } break; - case MSG_REFRESH_STATS: - mStatsHelper.clearStats(); - refreshStats(); } super.handleMessage(msg); } diff --git a/src/com/android/settings/fuelgauge/PowerWhitelistBackend.java b/src/com/android/settings/fuelgauge/PowerWhitelistBackend.java new file mode 100644 index 0000000..7199af8 --- /dev/null +++ b/src/com/android/settings/fuelgauge/PowerWhitelistBackend.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.fuelgauge; + +import android.os.IDeviceIdleController; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.ArraySet; +import android.util.Log; + + +/** + * Handles getting/changing the whitelist for the exceptions to battery saving features. + */ +public class PowerWhitelistBackend { + + private static final String TAG = "PowerWhitelistBackend"; + + private static final String DEVICE_IDLE_SERVICE = "deviceidle"; + + private static final PowerWhitelistBackend INSTANCE = new PowerWhitelistBackend(); + + private final IDeviceIdleController mDeviceIdleService; + private final ArraySet<String> mWhitelistedApps = new ArraySet<>(); + private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>(); + + public PowerWhitelistBackend() { + mDeviceIdleService = IDeviceIdleController.Stub.asInterface( + ServiceManager.getService(DEVICE_IDLE_SERVICE)); + refreshList(); + } + + public int getWhitelistSize() { + return mWhitelistedApps.size(); + } + + public boolean isSysWhitelisted(String pkg) { + return mSysWhitelistedApps.contains(pkg); + } + + public boolean isWhitelisted(String pkg) { + return mWhitelistedApps.contains(pkg); + } + + public void addApp(String pkg) { + try { + mDeviceIdleService.addPowerSaveWhitelistApp(pkg); + mWhitelistedApps.add(pkg); + } catch (RemoteException e) { + Log.w(TAG, "Unable to reach IDeviceIdleController", e); + } + } + + public void removeApp(String pkg) { + try { + mDeviceIdleService.removePowerSaveWhitelistApp(pkg); + mWhitelistedApps.remove(pkg); + } catch (RemoteException e) { + Log.w(TAG, "Unable to reach IDeviceIdleController", e); + } + } + + private void refreshList() { + mSysWhitelistedApps.clear(); + mWhitelistedApps.clear(); + try { + String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist(); + for (String app : whitelistedApps) { + mWhitelistedApps.add(app); + } + String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist(); + for (String app : sysWhitelistedApps) { + mSysWhitelistedApps.add(app); + } + } catch (RemoteException e) { + Log.w(TAG, "Unable to reach IDeviceIdleController", e); + } + } + + public static PowerWhitelistBackend getInstance() { + return INSTANCE; + } + +} |