Merge "Delete about phone v1 code"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8eaf761..6ccb8d1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2573,7 +2573,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.PictureInPictureSettings" />
+ android:value="com.android.settings.applications.appinfo.PictureInPictureSettings" />
</activity>
<activity android:name="Settings$AppPictureInPictureSettingsActivity"
@@ -2585,7 +2585,7 @@
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.PictureInPictureDetails" />
+ android:value="com.android.settings.applications.appinfo.PictureInPictureDetails" />
</activity>
<activity android:name="Settings$ZenAccessSettingsActivity"
@@ -2939,7 +2939,7 @@
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.DrawOverlayDetails" />
+ android:value="com.android.settings.applications.appinfo.DrawOverlayDetails" />
</activity>
<activity android:name="Settings$WriteSettingsActivity"
@@ -2963,7 +2963,7 @@
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.WriteSettingsDetails" />
+ android:value="com.android.settings.applications.appinfo.WriteSettingsDetails" />
</activity>
<activity android:name="Settings$ManageExternalSourcesActivity"
@@ -2986,7 +2986,7 @@
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.applications.ExternalSourcesDetails" />
+ android:value="com.android.settings.applications.appinfo.ExternalSourcesDetails" />
</activity>
<activity android:name="ShowAdminSupportDetailsDialog"
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index cbcdf48..6fa2042 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -2,6 +2,18 @@
<issues format="4">
<issue
+ id="LintError"
+ severity="Error"
+ message="No `.class` files were found in project ".", so none of the classfile based checks could be run. Does the project need to be built first?"
+ category="Lint"
+ priority="10"
+ summary="Lint Failure"
+ explanation="This issue type represents a problem running lint itself. Examples include failure to find bytecode for source files (which means certain detectors could not be run), parsing errors in lint configuration files, etc.
These errors are not errors in your own code, but they are shown to make it clear that some checks were not completed.">
+ <location
+ file="."/>
+ </issue>
+
+ <issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
@@ -93,7 +105,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/layout-land/choose_lock_pattern.xml"
- line="152"
+ line="160"
column="17"/>
</issue>
@@ -1085,7 +1097,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/layout-land/confirm_lock_pattern_internal.xml"
- line="110"
+ line="111"
column="17"/>
</issue>
@@ -1289,6 +1301,134 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#19263238" android:offset="0.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_color_inversion.xml"
+ line="17"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#00212121" android:offset="1.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_color_inversion.xml"
+ line="18"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#19FFFFFF" android:offset="0.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_color_inversion.xml"
+ line="32"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#00FFFFFF" android:offset="1.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_color_inversion.xml"
+ line="33"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#19263238" android:offset="0.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_daltonizer.xml"
+ line="16"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#00212121" android:offset="1.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_daltonizer.xml"
+ line="17"
+ column="27"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#19FFFFFF" android:offset="0.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_daltonizer.xml"
+ line="36"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" <item android:color="#00FFFFFF" android:offset="1.0"/>"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_daltonizer.xml"
+ line="37"
+ column="23"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" android:tint="@color/wifi_details_icon_color">"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
@@ -1869,7 +2009,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/layout/settings_main_dashboard.xml"
- line="29"
+ line="30"
column="9"/>
</issue>
@@ -1933,7 +2073,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rXC/strings.xml"
- line="2335"
+ line="2333"
column="168"/>
</issue>
@@ -1949,7 +2089,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rAU/strings.xml"
- line="2341"
+ line="2334"
column="64"/>
</issue>
@@ -1965,7 +2105,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rCA/strings.xml"
- line="2341"
+ line="2334"
column="64"/>
</issue>
@@ -1981,7 +2121,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rGB/strings.xml"
- line="2341"
+ line="2334"
column="64"/>
</issue>
@@ -1997,7 +2137,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values-en-rIN/strings.xml"
- line="2341"
+ line="2334"
column="64"/>
</issue>
@@ -2013,7 +2153,7 @@
errorLine2=" ~~~~~~~~~~~~~~~~~~~">
<location
file="res/values/strings.xml"
- line="5501"
+ line="5512"
column="36"/>
</issue>
@@ -2029,7 +2169,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="245"
+ line="246"
column="41"/>
</issue>
@@ -2045,7 +2185,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="364"
+ line="366"
column="42"/>
</issue>
@@ -2061,7 +2201,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="441"
+ line="443"
column="44"/>
</issue>
@@ -2077,7 +2217,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="447"
+ line="449"
column="44"/>
</issue>
@@ -2093,7 +2233,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="448"
+ line="450"
column="44"/>
</issue>
@@ -2109,7 +2249,7 @@
errorLine2=" ^">
<location
file="res/values/styles.xml"
- line="471"
+ line="473"
column="41"/>
</issue>
diff --git a/res/color/battery_icon_color_error.xml b/res/color/battery_icon_color_error.xml
index 3a71aae..99c7d7d 100644
--- a/res/color/battery_icon_color_error.xml
+++ b/res/color/battery_icon_color_error.xml
@@ -14,6 +14,6 @@
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="@*android:dimen/secondary_content_alpha_material_dark"
+ <item android:alpha="?android:attr/secondaryContentAlpha"
android:color="?android:attr/colorError"/>
</selector>
\ No newline at end of file
diff --git a/res/drawable/ic_color_inversion.xml b/res/drawable/ic_color_inversion.xml
new file mode 100644
index 0000000..aa59f5a
--- /dev/null
+++ b/res/drawable/ic_color_inversion.xml
@@ -0,0 +1,52 @@
+<!--
+ Copyright 2017 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.
+-->
+<vector android:height="192dp" android:viewportHeight="192.0"
+ android:viewportWidth="192.0" android:width="192dp"
+ xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#546E7A" android:pathData="M37.2,173.6l-28.5,-90c-1.7,-5.8 0.3,-12 5,-15.6l73.6,-57.1c5.2,-4 12.5,-4 17.6,0.1L178.6,70c4.6,3.7 6.5,9.9 4.8,15.6l-28.5,88.2c-1.9,6.1 -7.4,10.2 -13.7,10.2H50.8C44.5,183.9 39,179.7 37.2,173.6z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#263238"
+ android:pathData="M183.3,84.5l-28.5,88.2c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.9c-6.3,0 -11.9,-4.2 -13.7,-10.3l-28.5,-90C8.2,81.4 8,80.1 8,78.9c-0.1,1.6 0.1,3.1 0.6,4.7l28.5,90c1.8,6.1 7.4,10.3 13.7,10.3h90.3c6.3,0 11.8,-4.1 13.7,-10.2l28.5,-88.2c0.5,-1.6 0.7,-3.2 0.6,-4.7C183.9,82 183.7,83.3 183.3,84.5z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+ android:pathData="M13.6,69l73.6,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c3.4,2.7 5.3,6.7 5.4,10.8c0.2,-4.5 -1.8,-8.9 -5.4,-11.8L104.8,11c-5.2,-4.1 -12.4,-4.1 -17.6,-0.1L13.5,68C9.8,70.9 7.8,75.4 8,80C8.1,75.8 10.1,71.7 13.6,69z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+ android:pathData="M53.1,129.9l5,-4.8l-13.9,-13.3c0,0.8 -0.1,1.5 -0.2,2.3c-0.4,22.3 17.2,40.2 39.9,41.7l0.1,-5.3C70.2,149.5 58.6,141.4 53.1,129.9z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+ android:pathData="M108.7,51.8l-0.1,5.3c13.6,1 25,9.2 30.4,20.7l-4.9,4.8l13.6,13.3c0.1,-0.8 0.2,-1.5 0.2,-2.3C148.3,71.2 131,53.2 108.7,51.8z" android:strokeAlpha="0.2"/>
+ <path android:pathData="M154.9,173.7l13.7,-41.7l-49.5,-49.6l-18.6,-7.2l-19.6,3.1L65.9,98.6L74,128l55.9,55.9h11.2C147.5,183.9 153.1,179.8 154.9,173.7z">
+ <aapt:attr name="android:fillColor">
+ <gradient android:endX="139.008" android:endY="147.6131"
+ android:startX="98.1033" android:startY="106.7084" android:type="linear">
+ <item android:color="#19263238" android:offset="0.0"/>
+ <item android:color="#00212121" android:offset="1.0"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path android:fillAlpha="0.1" android:fillColor="#FF000000"
+ android:pathData="M70.3,104.5c0,0.2 0,0.3 0,0.5c0.3,-13.9 11.6,-25.1 25.6,-25.1v-1C81.8,78.9 70.3,90.4 70.3,104.5z" android:strokeAlpha="0.1"/>
+ <path android:fillAlpha="0.1" android:fillColor="#FF000000"
+ android:pathData="M95.9,136.5c-17.5,0 -31.7,-14 -32,-31.5c0,0.2 0,0.3 0,0.5c0,17.7 14.3,32 32,32c17.7,0 32,-14.3 32,-32c0,-0.2 0,-0.3 0,-0.5C127.6,122.5 113.4,136.5 95.9,136.5z" android:strokeAlpha="0.1"/>
+ <path android:fillColor="#FFFFFF" android:pathData="M95.9,72.5c-17.7,0 -32,14.3 -32,32c0,17.7 14.3,32 32,32c17.7,0 32,-14.3 32,-32S113.6,72.5 95.9,72.5zM70.3,104.5c0,-14.1 11.5,-25.6 25.6,-25.6v51.2C81.8,130.1 70.3,118.6 70.3,104.5z"/>
+ <path android:pathData="M37.1,173.6l-28.5,-90c-1.7,-5.8 0.3,-12 5,-15.6l73.5,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.5,88.2c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.8C44.5,183.9 38.9,179.7 37.1,173.6z">
+ <aapt:attr name="android:fillColor">
+ <gradient android:centerX="21.9873"
+ android:centerY="23.7751"
+ android:gradientRadius="158.0384" android:type="radial">
+ <item android:color="#19FFFFFF" android:offset="0.0"/>
+ <item android:color="#00FFFFFF" android:offset="1.0"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+</vector>
diff --git a/res/drawable/ic_daltonizer.xml b/res/drawable/ic_daltonizer.xml
new file mode 100644
index 0000000..04e397d
--- /dev/null
+++ b/res/drawable/ic_daltonizer.xml
@@ -0,0 +1,56 @@
+<!--
+ Copyright 2017 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.
+-->
+<vector android:height="192dp" android:viewportHeight="192.0"
+ android:viewportWidth="192.0" android:width="192dp"
+ xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#00BCD4" android:pathData="M37.1,173.5l-28.5,-90c-1.7,-5.8 0.3,-12 5,-15.6l73.6,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.5,88.2c-1.9,6.1 -7.4,10.2 -13.7,10.2H50.8C44.5,183.8 39,179.6 37.1,173.5z"/>
+ <path android:fillAlpha="0.2" android:fillColor="#263238"
+ android:pathData="M183.3,84.3l-28.5,88.2c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.8c-6.3,0 -11.9,-4.2 -13.7,-10.3l-28.5,-90C8.2,81.2 8,79.9 8,78.7c-0.1,1.6 0.1,3.1 0.6,4.7l28.5,90c1.8,6.1 7.4,10.3 13.7,10.3h90.3c6.3,0 11.8,-4.1 13.7,-10.2l28.5,-88.2c0.5,-1.6 0.7,-3.2 0.6,-4.7C183.9,81.8 183.7,83.2 183.3,84.3z" android:strokeAlpha="0.2"/>
+ <path android:fillAlpha="0.2" android:fillColor="#FFFFFF"
+ android:pathData="M13.5,68.8l73.6,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c3.4,2.7 5.3,6.7 5.4,10.8c0.2,-4.5 -1.8,-8.9 -5.4,-11.8l-73.8,-58.9c-5.2,-4.1 -12.4,-4.1 -17.6,-0.1L13.5,67.8c-3.7,2.9 -5.7,7.4 -5.5,12C8.1,75.6 10.1,71.5 13.5,68.8z" android:strokeAlpha="0.2"/>
+ <group>
+ <clip-path android:pathData="M37.1,173.5l-28.5,-90c-1.7,-5.8 0.3,-12 5,-15.6l73.6,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.5,88.2c-1.9,6.1 -7.4,10.2 -13.7,10.2H50.8C44.5,183.8 39,179.6 37.1,173.5z M 0,0"/>
+ <path android:pathData="M131.3,74.7L105,79.3l-5,-0.2l-3.8,2.4l-30.8,34.4L60.5,136l47.8,47.8 h32.9c6.3,0,11.8,-4.1,13.7,-10.2l18.4,-56.9L131.3,74.7z">
+ <aapt:attr name="android:fillColor">
+ <gradient android:endX="151.1709"
+ android:endY="160.5809" android:startX="91.7117"
+ android:startY="101.1217" android:type="linear">
+ <item android:color="#19263238" android:offset="0.0"/>
+ <item android:color="#00212121" android:offset="1.0"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ </group>
+ <path android:fillAlpha="0.1" android:fillColor="#263238"
+ android:pathData="M79.5,136l-18.9,0l1,1l18.9,0l35.6,-35.6l-1,-1z" android:strokeAlpha="0.1"/>
+ <path android:fillAlpha="0.1" android:fillColor="#263238"
+ android:pathData="M68.59,120.37l32.17,-32.17l0.99,0.99l-32.17,32.17z" android:strokeAlpha="0.1"/>
+ <path android:fillAlpha="0.1" android:fillColor="#263238"
+ android:pathData="M132.3,75.6l-1,-1c1.6,1.6 1.6,4.1 0,5.7l-12.4,12.4l1,1l12.4,-12.4C133.8,79.7 133.8,77.2 132.3,75.6z" android:strokeAlpha="0.1"/>
+ <path android:fillAlpha="0.1" android:fillColor="#263238"
+ android:pathData="M120.75,106.06l5.66,-5.66l0.99,0.99l-5.66,5.66z" android:strokeAlpha="0.1"/>
+ <path android:fillColor="#0097A7" android:pathData="M68.56,120.37l32.17,-32.17l7.64,7.64l-32.17,32.17z"/>
+ <path android:fillColor="#FFFFFF" android:pathData="M131.3,74.6l-9.3,-9.3c-1.6,-1.6 -4.1,-1.6 -5.6,0l-12.4,12.4l-7.7,-7.6l-5.6,5.6l5.7,5.7L60.6,117v19h19l35.6,-35.6l5.7,5.7l5.6,-5.6l-7.7,-7.7l12.4,-12.4C132.8,78.7 132.8,76.2 131.3,74.6zM76.2,128l-7.7,-7.7l32.2,-32.2l7.7,7.7L76.2,128z"/>
+ <path android:pathData="M37.1,173.5l-28.5,-90c-1.7,-5.8 0.3,-12 5,-15.6l73.5,-57.1c5.2,-4 12.5,-4 17.6,0.1l73.8,58.9c4.6,3.7 6.5,9.9 4.8,15.6l-28.5,88.2c-1.8,6.1 -7.4,10.2 -13.7,10.2H50.8C44.5,183.8 38.9,179.6 37.1,173.5z">
+ <aapt:attr name="android:fillColor">
+ <gradient android:centerX="21.977" android:centerY="23.6809"
+ android:gradientRadius="158.0384" android:type="radial">
+ <item android:color="#19FFFFFF" android:offset="0.0"/>
+ <item android:color="#00FFFFFF" android:offset="1.0"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+</vector>
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index 7728a07..d7d2e02 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -118,12 +118,14 @@
<Preference
android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment"
android:key="daltonizer_preference_screen"
- android:title="@string/accessibility_display_daltonizer_preference_title" />
+ android:title="@string/accessibility_display_daltonizer_preference_title"
+ android:icon="@drawable/ic_daltonizer"/>
<SwitchPreference
android:key="toggle_inversion_preference"
android:title="@string/accessibility_display_inversion_preference_title"
android:summary="@string/accessibility_display_inversion_preference_subtitle"
- android:persistent="false" />
+ android:persistent="false"
+ android:icon="@drawable/ic_color_inversion"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index 0611b09..2161415 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -42,6 +42,7 @@
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
+import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.content.PackageMonitor;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.view.RotationPolicy;
@@ -57,6 +58,7 @@
import com.android.settingslib.accessibility.AccessibilityUtils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -172,13 +174,7 @@
}
};
- private final SettingsContentObserver mSettingsContentObserver =
- new SettingsContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- updateServicePreferences();
- }
- };
+ private final SettingsContentObserver mSettingsContentObserver;
private final RotationPolicyListener mRotationPolicyListener = new RotationPolicyListener() {
@Override
@@ -224,6 +220,22 @@
.getBoolean(com.android.internal.R.bool.config_setColorTransformAccelerated);
}
+ public AccessibilitySettings() {
+ // Observe changes to anything that the shortcut can toggle, so we can reflect updates
+ final Collection<AccessibilityShortcutController.ToggleableFrameworkFeatureInfo> features =
+ AccessibilityShortcutController.getFrameworkShortcutFeaturesMap().values();
+ final List<String> shortcutFeatureKeys = new ArrayList<>(features.size());
+ for (AccessibilityShortcutController.ToggleableFrameworkFeatureInfo feature : features) {
+ shortcutFeatureKeys.add(feature.getSettingKey());
+ }
+ mSettingsContentObserver = new SettingsContentObserver(mHandler, shortcutFeatureKeys) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateAllPreferences();
+ }
+ };
+ }
+
@Override
public int getMetricsCategory() {
return MetricsEvent.ACCESSIBILITY;
diff --git a/src/com/android/settings/accessibility/SettingsContentObserver.java b/src/com/android/settings/accessibility/SettingsContentObserver.java
index c3baec5..de67f6c 100644
--- a/src/com/android/settings/accessibility/SettingsContentObserver.java
+++ b/src/com/android/settings/accessibility/SettingsContentObserver.java
@@ -22,16 +22,28 @@
import android.os.Handler;
import android.provider.Settings;
+import java.util.ArrayList;
+import java.util.List;
+
abstract class SettingsContentObserver extends ContentObserver {
+ private final List<String> mKeysToObserve = new ArrayList<>(2);
+
public SettingsContentObserver(Handler handler) {
super(handler);
+ mKeysToObserve.add(Settings.Secure.ACCESSIBILITY_ENABLED);
+ mKeysToObserve.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ }
+
+ public SettingsContentObserver(Handler handler, List<String> keysToObserve) {
+ this(handler);
+ mKeysToObserve.addAll(keysToObserve);
}
public void register(ContentResolver contentResolver) {
- contentResolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ACCESSIBILITY_ENABLED), false, this);
- contentResolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES), false, this);
+ for (int i = 0; i < mKeysToObserve.size(); i++) {
+ contentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(mKeysToObserve.get(i)), false, this);
+ }
}
public void unregister(ContentResolver contentResolver) {
diff --git a/src/com/android/settings/accessibility/ShortcutServicePickerFragment.java b/src/com/android/settings/accessibility/ShortcutServicePickerFragment.java
index e0c41be..52c1a0d 100644
--- a/src/com/android/settings/accessibility/ShortcutServicePickerFragment.java
+++ b/src/com/android/settings/accessibility/ShortcutServicePickerFragment.java
@@ -16,6 +16,8 @@
package com.android.settings.accessibility;
import static android.content.DialogInterface.BUTTON_POSITIVE;
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
@@ -85,9 +87,16 @@
Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureInfoMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
for (ComponentName componentName : frameworkFeatureInfoMap.keySet()) {
- // Lookup icon
+ final int iconId;
+ if (componentName.equals(COLOR_INVERSION_COMPONENT_NAME)) {
+ iconId = R.drawable.ic_color_inversion;
+ } else if (componentName.equals(DALTONIZER_COMPONENT_NAME)) {
+ iconId = R.drawable.ic_daltonizer;
+ } else {
+ iconId = R.drawable.empty_icon;
+ }
candidates.add(new FrameworkCandidateInfo(frameworkFeatureInfoMap.get(componentName),
- R.drawable.empty_icon, componentName.flattenToString()));
+ iconId, componentName.flattenToString()));
}
for (int i = 0; i < numInstalledServices; i++) {
final AccessibilityServiceInfo installedServiceInfo = installedServices.get(i);
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index d341d53..017afe7 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.applications.appinfo;
import android.app.LoaderManager;
-import android.app.slice.Slice;
import android.content.Context;
import android.content.Loader;
import android.content.pm.PackageInfo;
@@ -82,11 +81,6 @@
}
@Override
- public Slice getSettingSlice() {
- return null;
- }
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
diff --git a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
index 0d6c038..eac0a0c 100644
--- a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java
@@ -16,7 +16,6 @@
package com.android.settings.applications.appinfo;
-import android.app.slice.Slice;
import android.content.Context;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
@@ -51,11 +50,6 @@
}
@Override
- public Slice getSettingSlice() {
- return null;
- }
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
diff --git a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
index 2a20f80..3943041 100644
--- a/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppMemoryPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.applications.appinfo;
import android.app.Activity;
-import android.app.slice.Slice;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.AsyncTask;
@@ -111,11 +110,6 @@
}
@Override
- public Slice getSettingSlice() {
- return null;
- }
-
- @Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
diff --git a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
index bd309c6..815e8d8 100644
--- a/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppPermissionPreferenceController.java
@@ -23,13 +23,11 @@
import android.icu.text.ListFormatter;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceScreen;
import android.util.Log;
import com.android.settings.R;
import com.android.settings.applications.AppInfoDashboardFragment;
import com.android.settingslib.applications.PermissionsSummaryHelper;
-import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
diff --git a/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
index 3311daa..fa67ec8 100644
--- a/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
+++ b/src/com/android/settings/applications/appinfo/DefaultAppShortcutPreferenceControllerBase.java
@@ -51,11 +51,6 @@
}
@Override
- public Slice getSettingSlice() {
- return null;
- }
-
- @Override
public void updateState(Preference preference) {
preference.setSummary(isDefaultApp() ? R.string.yes : R.string.no);
}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
index a7d65d3..94aa608 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultHomePreferenceController.java
@@ -100,7 +100,7 @@
Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
.setPackage(packageName)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
return mPackageManager.queryIntentActivities(intent, 0).size() == 1 ? intent : null;
}
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index b3d9878..01d98b8 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -14,14 +14,10 @@
package com.android.settings.core;
import android.annotation.IntDef;
-import android.app.slice.Slice;
import android.content.Context;
-import android.support.v7.preference.Preference;
import android.text.TextUtils;
import android.util.Log;
-
-import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.search.ResultPayload;
import com.android.settings.search.SearchIndexableRaw;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -88,11 +84,6 @@
@AvailabilityStatus
public abstract int getAvailabilityStatus();
- /**
- * @return A slice for the corresponding setting.
- */
- public abstract Slice getSettingSlice();
-
@Override
public String getPreferenceKey() {
return mPreferenceKey;
@@ -150,10 +141,4 @@
public ResultPayload getResultPayload() {
return null;
}
-
- // TODO (b/69380366) Add Method to get preference UI
-
- // TODO (b/69380464) Add method to get intent
-
- // TODO (b/69380560) Add method to get broadcast intent
}
\ No newline at end of file
diff --git a/src/com/android/settings/core/FeatureFlags.java b/src/com/android/settings/core/FeatureFlags.java
index 7b39bdb..4371b4a 100644
--- a/src/com/android/settings/core/FeatureFlags.java
+++ b/src/com/android/settings/core/FeatureFlags.java
@@ -25,4 +25,5 @@
public static final String APP_INFO_V2 = "settings_app_info_v2";
public static final String CONNECTED_DEVICE_V2 = "settings_connected_device_v2";
public static final String BATTERY_SETTINGS_V2 = "settings_battery_v2";
+ public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
}
diff --git a/src/com/android/settings/core/TogglePreferenceController.java b/src/com/android/settings/core/TogglePreferenceController.java
index 03106d3..99d2ecc 100644
--- a/src/com/android/settings/core/TogglePreferenceController.java
+++ b/src/com/android/settings/core/TogglePreferenceController.java
@@ -13,7 +13,6 @@
*/
package com.android.settings.core;
-import android.app.slice.Slice;
import android.content.Context;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.preference.Preference;
@@ -55,10 +54,4 @@
setChecked(auto);
return true;
}
-
- @Override
- public Slice getSettingSlice() {
- // TODO
- return null;
- }
}
\ No newline at end of file
diff --git a/src/com/android/settings/display/ThemePreferenceController.java b/src/com/android/settings/display/ThemePreferenceController.java
index 3bb58b1..d1341dd74 100644
--- a/src/com/android/settings/display/ThemePreferenceController.java
+++ b/src/com/android/settings/display/ThemePreferenceController.java
@@ -125,7 +125,7 @@
private boolean isChangeableOverlay(String packageName) {
try {
PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
- return pi != null && !pi.isStaticOverlay;
+ return pi != null && !pi.isStaticOverlayPackage();
} catch (PackageManager.NameNotFoundException e) {
return false;
}
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
new file mode 100644
index 0000000..5d95dd2
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2017 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.app.Fragment;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceGroup;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatterySipper.DrainType;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.internal.os.PowerProfile;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.Utils;
+import com.android.settings.core.instrumentation.MetricsFeatureProvider;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controller that update the battery header view
+ */
+public class BatteryAppListPreferenceController extends AbstractPreferenceController
+ implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
+ private static final boolean USE_FAKE_DATA = true;
+ private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
+ private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
+ private static final int STATS_TYPE = BatteryStats.STATS_SINCE_CHARGED;
+
+ private final String mPreferenceKey;
+ @VisibleForTesting
+ PreferenceGroup mAppListGroup;
+ private BatteryStatsHelper mBatteryStatsHelper;
+ private ArrayMap<String, Preference> mPreferenceCache;
+ @VisibleForTesting
+ BatteryUtils mBatteryUtils;
+ private UserManager mUserManager;
+ private SettingsActivity mActivity;
+ private PreferenceFragment mFragment;
+ private Context mPrefContext;
+ SparseArray<List<Anomaly>> mAnomalySparseArray;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case BatteryEntry.MSG_UPDATE_NAME_ICON:
+ BatteryEntry entry = (BatteryEntry) msg.obj;
+ PowerGaugePreference pgp =
+ (PowerGaugePreference) mAppListGroup.findPreference(
+ Integer.toString(entry.sipper.uidObj.getUid()));
+ if (pgp != null) {
+ final int userId = UserHandle.getUserId(entry.sipper.getUid());
+ final UserHandle userHandle = new UserHandle(userId);
+ pgp.setIcon(mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle));
+ pgp.setTitle(entry.name);
+ if (entry.sipper.drainType == DrainType.APP) {
+ pgp.setContentDescription(entry.name);
+ }
+ }
+ break;
+ case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
+ Activity activity = mActivity;
+ if (activity != null) {
+ activity.reportFullyDrawn();
+ }
+ break;
+ }
+ super.handleMessage(msg);
+ }
+ };
+
+ public BatteryAppListPreferenceController(Context context, String preferenceKey,
+ Lifecycle lifecycle, SettingsActivity activity, PreferenceFragment fragment) {
+ super(context);
+
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+
+ mPreferenceKey = preferenceKey;
+ mBatteryUtils = BatteryUtils.getInstance(context);
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mActivity = activity;
+ mFragment = fragment;
+ }
+
+ @Override
+ public void onPause() {
+ BatteryEntry.stopRequestQueue();
+ mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mActivity.isChangingConfigurations()) {
+ BatteryEntry.clearUidCache();
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPrefContext = screen.getContext();
+ mAppListGroup = (PreferenceGroup) screen.findPreference(mPreferenceKey);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return FeatureFlagUtils.isEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return mPreferenceKey;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (preference instanceof PowerGaugePreference) {
+ PowerGaugePreference pgp = (PowerGaugePreference) preference;
+ BatteryEntry entry = pgp.getInfo();
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
+ mFragment, mBatteryStatsHelper, STATS_TYPE, entry, pgp.getPercent(),
+ mAnomalySparseArray != null ? mAnomalySparseArray.get(entry.sipper.getUid())
+ : null);
+ return true;
+ }
+ return false;
+ }
+
+ public void refreshAnomalyIcon(final SparseArray<List<Anomaly>> anomalySparseArray) {
+ if (!isAvailable()) {
+ return;
+ }
+ mAnomalySparseArray = anomalySparseArray;
+ for (int i = 0, size = anomalySparseArray.size(); i < size; i++) {
+ final String key = extractKeyFromUid(anomalySparseArray.keyAt(i));
+ final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
+ key);
+ if (pref != null) {
+ pref.shouldShowAnomalyIcon(true);
+ }
+ }
+ }
+
+ public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps,
+ CharSequence timeSequence) {
+ if (!isAvailable()) {
+ return;
+ }
+ mBatteryStatsHelper = statsHelper;
+ final int resId = showAllApps ? R.string.power_usage_list_summary_device
+ : R.string.power_usage_list_summary;
+ mAppListGroup.setTitle(TextUtils.expandTemplate(mContext.getText(resId), timeSequence));
+
+ final PowerProfile powerProfile = statsHelper.getPowerProfile();
+ final BatteryStats stats = statsHelper.getStats();
+ final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
+ boolean addedSome = false;
+ final int dischargeAmount = USE_FAKE_DATA ? 5000
+ : stats != null ? stats.getDischargeAmount(STATS_TYPE) : 0;
+
+ cacheRemoveAllPrefs(mAppListGroup);
+ mAppListGroup.setOrderingAsAdded(false);
+
+ if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
+ final List<BatterySipper> usageList = getCoalescedUsageList(
+ USE_FAKE_DATA ? getFakeStats() : statsHelper.getUsageList());
+ double hiddenPowerMah = showAllApps ? 0 :
+ mBatteryUtils.removeHiddenBatterySippers(usageList);
+ mBatteryUtils.sortUsageList(usageList);
+
+ final int numSippers = usageList.size();
+ for (int i = 0; i < numSippers; i++) {
+ final BatterySipper sipper = usageList.get(i);
+ double totalPower = USE_FAKE_DATA ? 4000 : statsHelper.getTotalPower();
+
+ final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
+ sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
+
+ if (((int) (percentOfTotal + .5)) < 1) {
+ continue;
+ }
+ if (shouldHideSipper(sipper)) {
+ continue;
+ }
+ final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
+ final BatteryEntry entry = new BatteryEntry(mActivity, mHandler, mUserManager,
+ sipper);
+ final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
+ userHandle);
+ final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
+ entry.getLabel(),
+ userHandle);
+
+ final String key = extractKeyFromSipper(sipper);
+ PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
+ if (pref == null) {
+ pref = new PowerGaugePreference(mPrefContext, badgedIcon,
+ contentDescription, entry);
+ pref.setKey(key);
+ }
+ sipper.percent = percentOfTotal;
+ pref.setTitle(entry.getLabel());
+ pref.setOrder(i + 1);
+ pref.setPercent(percentOfTotal);
+ pref.shouldShowAnomalyIcon(false);
+ if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
+ sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
+ BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, STATS_TYPE);
+ }
+ setUsageSummary(pref, sipper);
+ addedSome = true;
+ mAppListGroup.addPreference(pref);
+ if (mAppListGroup.getPreferenceCount() - getCachedCount()
+ > (MAX_ITEMS_TO_LIST + 1)) {
+ break;
+ }
+ }
+ }
+ if (!addedSome) {
+ addNotAvailableMessage();
+ }
+ removeCachedPrefs(mAppListGroup);
+
+ BatteryEntry.startRequestQueue();
+ }
+
+ /**
+ * We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
+ * exists for all users of the same app. We detect this case and merge the power use
+ * for dex2oat to the device OWNER's use of the app.
+ *
+ * @return A sorted list of apps using power.
+ */
+ private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
+ final SparseArray<BatterySipper> uidList = new SparseArray<>();
+
+ final ArrayList<BatterySipper> results = new ArrayList<>();
+ final int numSippers = sippers.size();
+ for (int i = 0; i < numSippers; i++) {
+ BatterySipper sipper = sippers.get(i);
+ if (sipper.getUid() > 0) {
+ int realUid = sipper.getUid();
+
+ // Check if this UID is a shared GID. If so, we combine it with the OWNER's
+ // actual app UID.
+ if (isSharedGid(sipper.getUid())) {
+ realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
+ UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
+ }
+
+ // Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
+ if (isSystemUid(realUid)
+ && !"mediaserver".equals(sipper.packageWithHighestDrain)) {
+ // Use the system UID for all UIDs running in their own sandbox that
+ // are not apps. We exclude mediaserver because we already are expected to
+ // report that as a separate item.
+ realUid = Process.SYSTEM_UID;
+ }
+
+ if (realUid != sipper.getUid()) {
+ // Replace the BatterySipper with a new one with the real UID set.
+ BatterySipper newSipper = new BatterySipper(sipper.drainType,
+ new FakeUid(realUid), 0.0);
+ newSipper.add(sipper);
+ newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
+ newSipper.mPackages = sipper.mPackages;
+ sipper = newSipper;
+ }
+
+ int index = uidList.indexOfKey(realUid);
+ if (index < 0) {
+ // New entry.
+ uidList.put(realUid, sipper);
+ } else {
+ // Combine BatterySippers if we already have one with this UID.
+ final BatterySipper existingSipper = uidList.valueAt(index);
+ existingSipper.add(sipper);
+ if (existingSipper.packageWithHighestDrain == null
+ && sipper.packageWithHighestDrain != null) {
+ existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
+ }
+
+ final int existingPackageLen = existingSipper.mPackages != null ?
+ existingSipper.mPackages.length : 0;
+ final int newPackageLen = sipper.mPackages != null ?
+ sipper.mPackages.length : 0;
+ if (newPackageLen > 0) {
+ String[] newPackages = new String[existingPackageLen + newPackageLen];
+ if (existingPackageLen > 0) {
+ System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
+ existingPackageLen);
+ }
+ System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
+ newPackageLen);
+ existingSipper.mPackages = newPackages;
+ }
+ }
+ } else {
+ results.add(sipper);
+ }
+ }
+
+ final int numUidSippers = uidList.size();
+ for (int i = 0; i < numUidSippers; i++) {
+ results.add(uidList.valueAt(i));
+ }
+
+ // The sort order must have changed, so re-sort based on total power use.
+ mBatteryUtils.sortUsageList(results);
+ return results;
+ }
+
+ @VisibleForTesting
+ void setUsageSummary(Preference preference, BatterySipper sipper) {
+ // Only show summary when usage time is longer than one minute
+ final long usageTimeMs = sipper.usageTimeMs;
+ if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
+ final CharSequence timeSequence = Utils.formatElapsedTime(mContext, usageTimeMs,
+ false);
+ preference.setSummary(
+ (sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
+ ? timeSequence
+ : TextUtils.expandTemplate(mContext.getText(R.string.battery_used_for),
+ timeSequence));
+ }
+ }
+
+ @VisibleForTesting
+ boolean shouldHideSipper(BatterySipper sipper) {
+ // Don't show over-counted and unaccounted in any condition
+ return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
+ || sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
+ }
+
+ @VisibleForTesting
+ String extractKeyFromSipper(BatterySipper sipper) {
+ if (sipper.uidObj != null) {
+ return extractKeyFromUid(sipper.getUid());
+ } else if (sipper.drainType == DrainType.USER) {
+ return sipper.drainType.toString() + sipper.userId;
+ } else if (sipper.drainType != DrainType.APP) {
+ return sipper.drainType.toString();
+ } else if (sipper.getPackages() != null) {
+ return TextUtils.concat(sipper.getPackages()).toString();
+ } else {
+ Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
+ return "-1";
+ }
+ }
+
+ @VisibleForTesting
+ String extractKeyFromUid(int uid) {
+ return Integer.toString(uid);
+ }
+
+ private void cacheRemoveAllPrefs(PreferenceGroup group) {
+ mPreferenceCache = new ArrayMap<>();
+ final int N = group.getPreferenceCount();
+ for (int i = 0; i < N; i++) {
+ Preference p = group.getPreference(i);
+ if (TextUtils.isEmpty(p.getKey())) {
+ continue;
+ }
+ mPreferenceCache.put(p.getKey(), p);
+ }
+ }
+
+ private static boolean isSharedGid(int uid) {
+ return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
+ }
+
+ private static boolean isSystemUid(int uid) {
+ final int appUid = UserHandle.getAppId(uid);
+ return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
+ }
+
+ private static List<BatterySipper> getFakeStats() {
+ ArrayList<BatterySipper> stats = new ArrayList<>();
+ float use = 5;
+ for (DrainType type : DrainType.values()) {
+ if (type == DrainType.APP) {
+ continue;
+ }
+ stats.add(new BatterySipper(type, null, use));
+ use += 5;
+ }
+ for (int i = 0; i < 100; i++) {
+ stats.add(new BatterySipper(DrainType.APP,
+ new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
+ }
+ stats.add(new BatterySipper(DrainType.APP,
+ new FakeUid(0), use));
+
+ // Simulate dex2oat process.
+ BatterySipper sipper = new BatterySipper(DrainType.APP,
+ new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
+ sipper.packageWithHighestDrain = "dex2oat";
+ stats.add(sipper);
+
+ sipper = new BatterySipper(DrainType.APP,
+ new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
+ sipper.packageWithHighestDrain = "dex2oat";
+ stats.add(sipper);
+
+ sipper = new BatterySipper(DrainType.APP,
+ new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
+ stats.add(sipper);
+
+ return stats;
+ }
+
+ private Preference getCachedPreference(String key) {
+ return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
+ }
+
+ private void removeCachedPrefs(PreferenceGroup group) {
+ for (Preference p : mPreferenceCache.values()) {
+ group.removePreference(p);
+ }
+ mPreferenceCache = null;
+ }
+
+ private int getCachedCount() {
+ return mPreferenceCache != null ? mPreferenceCache.size() : 0;
+ }
+
+ private void addNotAvailableMessage() {
+ final String NOT_AVAILABLE = "not_available";
+ Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
+ if (notAvailable == null) {
+ notAvailable = new Preference(mPrefContext);
+ notAvailable.setKey(NOT_AVAILABLE);
+ notAvailable.setTitle(R.string.power_usage_not_available);
+ mAppListGroup.addPreference(notAvailable);
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index ed5b6f4..e4b70a1 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -21,21 +21,13 @@
import android.app.LoaderManager.LoaderCallbacks;
import android.content.Context;
import android.content.Loader;
-import android.graphics.drawable.Drawable;
import android.os.BatteryStats;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Process;
-import android.os.UserHandle;
import android.provider.SearchIndexableResource;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceGroup;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.text.format.Formatter;
-import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
@@ -49,7 +41,6 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatterySipper.DrainType;
-import com.android.internal.os.PowerProfile;
import com.android.settings.R;
import com.android.settings.Settings.HighPowerApplicationsActivity;
import com.android.settings.SettingsActivity;
@@ -71,6 +62,7 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.ArrayList;
import java.util.Arrays;
@@ -86,12 +78,9 @@
static final String TAG = "PowerUsageSummary";
private static final boolean DEBUG = false;
- private static final boolean USE_FAKE_DATA = false;
private static final String KEY_APP_LIST = "app_list";
private static final String KEY_BATTERY_HEADER = "battery_header";
private static final String KEY_SHOW_ALL_APPS = "show_all_apps";
- private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 10;
- private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
private static final String KEY_SCREEN_USAGE = "screen_usage";
private static final String KEY_TIME_SINCE_LAST_FULL_CHARGE = "last_full_charge";
@@ -136,6 +125,7 @@
PreferenceGroup mAppListGroup;
@VisibleForTesting
BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
+ private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
private AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
private int mStatsType = BatteryStats.STATS_SINCE_CHARGED;
@@ -157,7 +147,7 @@
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
updateAnomalySparseArray(data);
- refreshAnomalyIcon();
+ mBatteryAppListPreferenceController.refreshAnomalyIcon(mAnomalySparseArray);
}
@Override
@@ -235,7 +225,6 @@
initFeatureProvider();
mBatteryLayoutPref = (LayoutPreference) findPreference(KEY_BATTERY_HEADER);
- mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
mScreenUsagePref = (PowerGaugePreference) findPreference(KEY_SCREEN_USAGE);
mLastFullChargePref = (PowerGaugePreference) findPreference(
KEY_TIME_SINCE_LAST_FULL_CHARGE);
@@ -255,21 +244,6 @@
}
@Override
- public void onPause() {
- BatteryEntry.stopRequestQueue();
- mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
- super.onPause();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (getActivity().isChangingConfigurations()) {
- BatteryEntry.clearUidCache();
- }
- }
-
- @Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(KEY_SHOW_ALL_APPS, mShowAllApps);
@@ -283,14 +257,7 @@
if (KEY_BATTERY_HEADER.equals(preference.getKey())) {
performBatteryHeaderClick();
return true;
- } else if (!(preference instanceof PowerGaugePreference)) {
- return super.onPreferenceTreeClick(preference);
}
- PowerGaugePreference pgp = (PowerGaugePreference) preference;
- BatteryEntry entry = pgp.getInfo();
- AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
- this, mStatsHelper, mStatsType, entry, pgp.getPercent(),
- mAnomalySparseArray.get(entry.sipper.getUid()));
return super.onPreferenceTreeClick(preference);
}
@@ -306,10 +273,15 @@
@Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
+ final Lifecycle lifecycle = getLifecycle();
+ final SettingsActivity activity = (SettingsActivity) getActivity();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mBatteryHeaderPreferenceController = new BatteryHeaderPreferenceController(
- context, getActivity(), this /* host */, getLifecycle());
+ context, activity, this /* host */, getLifecycle());
controllers.add(mBatteryHeaderPreferenceController);
+ mBatteryAppListPreferenceController = new BatteryAppListPreferenceController(context,
+ KEY_APP_LIST, lifecycle, activity, this);
+ controllers.add(mBatteryAppListPreferenceController);
controllers.add(new AutoBrightnessPreferenceController(context, KEY_AUTO_BRIGHTNESS));
controllers.add(new TimeoutPreferenceController(context, KEY_SCREEN_TIMEOUT));
controllers.add(new BatterySaverController(context, getLifecycle()));
@@ -388,17 +360,6 @@
}
}
- private void addNotAvailableMessage() {
- final String NOT_AVAILABLE = "not_available";
- Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
- if (notAvailable == null) {
- notAvailable = new Preference(getPrefContext());
- notAvailable.setKey(NOT_AVAILABLE);
- notAvailable.setTitle(R.string.power_usage_not_available);
- mAppListGroup.addPreference(notAvailable);
- }
- }
-
private void performBatteryHeaderClick() {
if (mPowerFeatureProvider.isAdvancedUiEnabled()) {
Utils.startWithFragment(getContext(), PowerUsageAdvanced.class.getName(), null,
@@ -415,101 +376,6 @@
}
}
- private static boolean isSharedGid(int uid) {
- return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
- }
-
- private static boolean isSystemUid(int uid) {
- final int appUid = UserHandle.getAppId(uid);
- return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
- }
-
- /**
- * We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
- * exists for all users of the same app. We detect this case and merge the power use
- * for dex2oat to the device OWNER's use of the app.
- *
- * @return A sorted list of apps using power.
- */
- private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
- final SparseArray<BatterySipper> uidList = new SparseArray<>();
-
- final ArrayList<BatterySipper> results = new ArrayList<>();
- final int numSippers = sippers.size();
- for (int i = 0; i < numSippers; i++) {
- BatterySipper sipper = sippers.get(i);
- if (sipper.getUid() > 0) {
- int realUid = sipper.getUid();
-
- // Check if this UID is a shared GID. If so, we combine it with the OWNER's
- // actual app UID.
- if (isSharedGid(sipper.getUid())) {
- realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
- UserHandle.getAppIdFromSharedAppGid(sipper.getUid()));
- }
-
- // Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
- if (isSystemUid(realUid)
- && !"mediaserver".equals(sipper.packageWithHighestDrain)) {
- // Use the system UID for all UIDs running in their own sandbox that
- // are not apps. We exclude mediaserver because we already are expected to
- // report that as a separate item.
- realUid = Process.SYSTEM_UID;
- }
-
- if (realUid != sipper.getUid()) {
- // Replace the BatterySipper with a new one with the real UID set.
- BatterySipper newSipper = new BatterySipper(sipper.drainType,
- new FakeUid(realUid), 0.0);
- newSipper.add(sipper);
- newSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
- newSipper.mPackages = sipper.mPackages;
- sipper = newSipper;
- }
-
- int index = uidList.indexOfKey(realUid);
- if (index < 0) {
- // New entry.
- uidList.put(realUid, sipper);
- } else {
- // Combine BatterySippers if we already have one with this UID.
- final BatterySipper existingSipper = uidList.valueAt(index);
- existingSipper.add(sipper);
- if (existingSipper.packageWithHighestDrain == null
- && sipper.packageWithHighestDrain != null) {
- existingSipper.packageWithHighestDrain = sipper.packageWithHighestDrain;
- }
-
- final int existingPackageLen = existingSipper.mPackages != null ?
- existingSipper.mPackages.length : 0;
- final int newPackageLen = sipper.mPackages != null ?
- sipper.mPackages.length : 0;
- if (newPackageLen > 0) {
- String[] newPackages = new String[existingPackageLen + newPackageLen];
- if (existingPackageLen > 0) {
- System.arraycopy(existingSipper.mPackages, 0, newPackages, 0,
- existingPackageLen);
- }
- System.arraycopy(sipper.mPackages, 0, newPackages, existingPackageLen,
- newPackageLen);
- existingSipper.mPackages = newPackages;
- }
- }
- } else {
- results.add(sipper);
- }
- }
-
- final int numUidSippers = uidList.size();
- for (int i = 0; i < numUidSippers; i++) {
- results.add(uidList.valueAt(i));
- }
-
- // The sort order must have changed, so re-sort based on total power use.
- mBatteryUtils.sortUsageList(results);
- return results;
- }
-
protected void refreshUi() {
final Context context = getContext();
if (context == null) {
@@ -527,102 +393,8 @@
final CharSequence timeSequence = Utils.formatRelativeTime(context, lastFullChargeTime,
false);
- final int resId = mShowAllApps ? R.string.power_usage_list_summary_device
- : R.string.power_usage_list_summary;
- mAppListGroup.setTitle(TextUtils.expandTemplate(getText(resId), timeSequence));
-
- refreshAppListGroup();
- }
-
- private void refreshAppListGroup() {
- final PowerProfile powerProfile = mStatsHelper.getPowerProfile();
- final BatteryStats stats = mStatsHelper.getStats();
- final double averagePower = powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL);
- boolean addedSome = false;
- final int dischargeAmount = USE_FAKE_DATA ? 5000
- : stats != null ? stats.getDischargeAmount(mStatsType) : 0;
-
- cacheRemoveAllPrefs(mAppListGroup);
- mAppListGroup.setOrderingAsAdded(false);
-
- if (averagePower >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP || USE_FAKE_DATA) {
- final List<BatterySipper> usageList = getCoalescedUsageList(
- USE_FAKE_DATA ? getFakeStats() : mStatsHelper.getUsageList());
- double hiddenPowerMah = mShowAllApps ? 0 :
- mBatteryUtils.removeHiddenBatterySippers(usageList);
- mBatteryUtils.sortUsageList(usageList);
-
- final int numSippers = usageList.size();
- for (int i = 0; i < numSippers; i++) {
- final BatterySipper sipper = usageList.get(i);
- double totalPower = USE_FAKE_DATA ? 4000 : mStatsHelper.getTotalPower();
-
- final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
- sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
-
- if (((int) (percentOfTotal + .5)) < 1) {
- continue;
- }
- if (shouldHideSipper(sipper)) {
- continue;
- }
- final UserHandle userHandle = new UserHandle(UserHandle.getUserId(sipper.getUid()));
- final BatteryEntry entry = new BatteryEntry(getActivity(), mHandler, mUm, sipper);
- final Drawable badgedIcon = mUm.getBadgedIconForUser(entry.getIcon(),
- userHandle);
- final CharSequence contentDescription = mUm.getBadgedLabelForUser(entry.getLabel(),
- userHandle);
-
- final String key = extractKeyFromSipper(sipper);
- PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
- if (pref == null) {
- pref = new PowerGaugePreference(getPrefContext(), badgedIcon,
- contentDescription, entry);
- pref.setKey(key);
- }
- sipper.percent = percentOfTotal;
- pref.setTitle(entry.getLabel());
- pref.setOrder(i + 1);
- pref.setPercent(percentOfTotal);
- pref.shouldShowAnomalyIcon(false);
- if (sipper.usageTimeMs == 0 && sipper.drainType == DrainType.APP) {
- sipper.usageTimeMs = mBatteryUtils.getProcessTimeMs(
- BatteryUtils.StatusType.FOREGROUND, sipper.uidObj, mStatsType);
- }
- setUsageSummary(pref, sipper);
- addedSome = true;
- mAppListGroup.addPreference(pref);
- if (mAppListGroup.getPreferenceCount() - getCachedCount()
- > (MAX_ITEMS_TO_LIST + 1)) {
- break;
- }
- }
- }
- if (!addedSome) {
- addNotAvailableMessage();
- }
- removeCachedPrefs(mAppListGroup);
-
- BatteryEntry.startRequestQueue();
- }
-
- @VisibleForTesting
- boolean shouldHideSipper(BatterySipper sipper) {
- // Don't show over-counted and unaccounted in any condition
- return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
- || sipper.drainType == BatterySipper.DrainType.UNACCOUNTED;
- }
-
- @VisibleForTesting
- void refreshAnomalyIcon() {
- for (int i = 0, size = mAnomalySparseArray.size(); i < size; i++) {
- final String key = extractKeyFromUid(mAnomalySparseArray.keyAt(i));
- final PowerGaugePreference pref = (PowerGaugePreference) mAppListGroup.findPreference(
- key);
- if (pref != null) {
- pref.shouldShowAnomalyIcon(true);
- }
- }
+ mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps,
+ timeSequence);
}
@VisibleForTesting
@@ -633,6 +405,11 @@
}
@VisibleForTesting
+ void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
+ mBatteryLayoutPref = layoutPreference;
+ }
+
+ @VisibleForTesting
AnomalyDetectionPolicy getAnomalyDetectionPolicy() {
return new AnomalyDetectionPolicy(getContext());
}
@@ -675,54 +452,6 @@
}
@VisibleForTesting
- double calculatePercentage(double powerUsage, double dischargeAmount) {
- final double totalPower = mStatsHelper.getTotalPower();
- return totalPower == 0 ? 0 :
- ((powerUsage / totalPower) * dischargeAmount);
- }
-
- @VisibleForTesting
- void setUsageSummary(Preference preference, BatterySipper sipper) {
- // Only show summary when usage time is longer than one minute
- final long usageTimeMs = sipper.usageTimeMs;
- if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
- final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), usageTimeMs,
- false);
- preference.setSummary(
- (sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
- ? timeSequence
- : TextUtils.expandTemplate(getText(R.string.battery_used_for),
- timeSequence));
- }
- }
-
- @VisibleForTesting
- String extractKeyFromSipper(BatterySipper sipper) {
- if (sipper.uidObj != null) {
- return extractKeyFromUid(sipper.getUid());
- } else if (sipper.drainType == DrainType.USER) {
- return sipper.drainType.toString() + sipper.userId;
- } else if (sipper.drainType != DrainType.APP) {
- return sipper.drainType.toString();
- } else if (sipper.getPackages() != null) {
- return TextUtils.concat(sipper.getPackages()).toString();
- } else {
- Log.w(TAG, "Inappropriate BatterySipper without uid and package names: " + sipper);
- return "-1";
- }
- }
-
- @VisibleForTesting
- String extractKeyFromUid(int uid) {
- return Integer.toString(uid);
- }
-
- @VisibleForTesting
- void setBatteryLayoutPreference(LayoutPreference layoutPreference) {
- mBatteryLayoutPref = layoutPreference;
- }
-
- @VisibleForTesting
void initFeatureProvider() {
final Context context = getContext();
mPowerFeatureProvider = FeatureFactory.getFactory(context)
@@ -755,72 +484,6 @@
}
}
- private static List<BatterySipper> getFakeStats() {
- ArrayList<BatterySipper> stats = new ArrayList<>();
- float use = 5;
- for (DrainType type : DrainType.values()) {
- if (type == DrainType.APP) {
- continue;
- }
- stats.add(new BatterySipper(type, null, use));
- use += 5;
- }
- for (int i = 0; i < 100; i++) {
- stats.add(new BatterySipper(DrainType.APP,
- new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
- }
- stats.add(new BatterySipper(DrainType.APP,
- new FakeUid(0), use));
-
- // Simulate dex2oat process.
- BatterySipper sipper = new BatterySipper(DrainType.APP,
- new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID)), 10.0f);
- sipper.packageWithHighestDrain = "dex2oat";
- stats.add(sipper);
-
- sipper = new BatterySipper(DrainType.APP,
- new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
- sipper.packageWithHighestDrain = "dex2oat";
- stats.add(sipper);
-
- sipper = new BatterySipper(DrainType.APP,
- new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
- stats.add(sipper);
-
- return stats;
- }
-
- Handler mHandler = new Handler() {
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case BatteryEntry.MSG_UPDATE_NAME_ICON:
- BatteryEntry entry = (BatteryEntry) msg.obj;
- PowerGaugePreference pgp =
- (PowerGaugePreference) findPreference(
- Integer.toString(entry.sipper.uidObj.getUid()));
- if (pgp != null) {
- final int userId = UserHandle.getUserId(entry.sipper.getUid());
- final UserHandle userHandle = new UserHandle(userId);
- pgp.setIcon(mUm.getBadgedIconForUser(entry.getIcon(), userHandle));
- pgp.setTitle(entry.name);
- if (entry.sipper.drainType == DrainType.APP) {
- pgp.setContentDescription(entry.name);
- }
- }
- break;
- case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
- Activity activity = getActivity();
- if (activity != null) {
- activity.reportFullyDrawn();
- }
- break;
- }
- super.handleMessage(msg);
- }
- };
-
@Override
public void onAnomalyHandled(Anomaly anomaly) {
mAnomalySummaryPreferenceController.hideHighUsagePreference();
diff --git a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
index 54b58d1..da2197c 100644
--- a/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/core/BasePreferenceControllerTest.java
@@ -117,4 +117,4 @@
assertThat(mPreferenceController.isSupported()).isTrue();
}
-}
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
new file mode 100644
index 0000000..a814989
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.support.v14.preference.PreferenceFragment;
+import android.support.v7.preference.PreferenceGroup;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.FeatureFlagUtils;
+import android.util.SparseArray;
+
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.SettingsShadowSystemProperties;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, shadows =
+ SettingsShadowSystemProperties.class)
+public class BatteryAppListPreferenceControllerTest {
+ private static final String[] PACKAGE_NAMES = {"com.app1", "com.app2"};
+ private static final String KEY_APP_LIST = "app_list";
+ private static final int UID = 123;
+
+ @Mock
+ private BatterySipper mNormalBatterySipper;
+ @Mock
+ private SettingsActivity mSettingsActivity;
+ @Mock
+ private PreferenceGroup mAppListGroup;
+ @Mock
+ private PreferenceFragment mFragment;
+ @Mock
+ private BatteryUtils mBatteryUtils;
+
+ private Context mContext;
+ private PowerGaugePreference mPreference;
+ private BatteryAppListPreferenceController mPreferenceController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ FakeFeatureFactory.setupForTest();
+
+ mPreference = new PowerGaugePreference(mContext);
+ when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
+ when(mNormalBatterySipper.getUid()).thenReturn(UID);
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ mPreferenceController = new BatteryAppListPreferenceController(mContext, KEY_APP_LIST, null,
+ mSettingsActivity, mFragment);
+ mPreferenceController.mBatteryUtils = mBatteryUtils;
+ mPreferenceController.mAppListGroup = mAppListGroup;
+ }
+
+ @Test
+ public void testExtractKeyFromSipper_typeAPPUidObjectNull_returnPackageNames() {
+ mNormalBatterySipper.uidObj = null;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
+ assertThat(key).isEqualTo(TextUtils.concat(mNormalBatterySipper.getPackages()).toString());
+ }
+
+ @Test
+ public void testExtractKeyFromSipper_typeOther_returnDrainType() {
+ mNormalBatterySipper.uidObj = null;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
+
+ final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
+ assertThat(key).isEqualTo(mNormalBatterySipper.drainType.toString());
+ }
+
+ @Test
+ public void testExtractKeyFromSipper_typeUser_returnDrainTypeWithUserId() {
+ mNormalBatterySipper.uidObj = null;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.USER;
+ mNormalBatterySipper.userId = 2;
+
+ final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
+ assertThat(key).isEqualTo("USER2");
+ }
+
+ @Test
+ public void testExtractKeyFromSipper_typeAPPUidObjectNotNull_returnUid() {
+ mNormalBatterySipper.uidObj = new BatteryStatsImpl.Uid(new BatteryStatsImpl(), UID);
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ final String key = mPreferenceController.extractKeyFromSipper(mNormalBatterySipper);
+ assertThat(key).isEqualTo(Integer.toString(mNormalBatterySipper.getUid()));
+ }
+
+ @Test
+ public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
+ mNormalBatterySipper.usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
+
+ mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+ assertThat(mPreference.getSummary()).isNull();
+ }
+
+ @Test
+ public void testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary() {
+ mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+ doReturn(mContext.getText(R.string.battery_used_for)).when(mFragment).getText(
+ R.string.battery_used_for);
+ doReturn(mContext).when(mFragment).getContext();
+
+ mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+
+ assertThat(mPreference.getSummary().toString()).isEqualTo("Used for 2m");
+ }
+
+ @Test
+ public void testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary() {
+ mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+ doReturn(true).when(mBatteryUtils).shouldHideSipper(mNormalBatterySipper);
+ doReturn(mContext).when(mFragment).getContext();
+
+ mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+
+ assertThat(mPreference.getSummary().toString()).isEqualTo("2m");
+ }
+
+ @Test
+ public void testSetUsageSummary_timeMoreThanOneMinute_notApp_setUsedSummary() {
+ mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.PHONE;
+ doReturn(mContext).when(mFragment).getContext();
+
+ mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+
+ assertThat(mPreference.getSummary().toString()).isEqualTo("2m");
+ }
+
+ @Test
+ public void testRefreshAnomalyIcon_containsAnomaly_showAnomalyIcon() {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
+ PowerGaugePreference preference = new PowerGaugePreference(mContext);
+ final String key = mPreferenceController.extractKeyFromUid(UID);
+ final SparseArray<List<Anomaly>> anomalySparseArray = new SparseArray<>();
+ anomalySparseArray.append(UID, null);
+ preference.setKey(key);
+ doReturn(preference).when(mAppListGroup).findPreference(key);
+
+ mPreferenceController.refreshAnomalyIcon(anomalySparseArray);
+
+ assertThat(preference.showAnomalyIcon()).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSipper_typeOvercounted_returnTrue() {
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
+
+ assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSipper_typeUnaccounted_returnTrue() {
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
+
+ assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ }
+
+ @Test
+ public void testShouldHideSipper_typeNormal_returnFalse() {
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isFalse();
+ }
+
+ @Test
+ public void testIsAvailable_featureOn_returnTrue() {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, true);
+
+ assertThat(mPreferenceController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void testIsAvailable_featureOff_returnFalse() {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.BATTERY_DISPLAY_APP_LIST, false);
+
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
new file mode 100644
index 0000000..0ca983f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2016 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 static com.android.settings.fuelgauge.PowerUsageSummary.MENU_HIGH_POWER_APPS;
+import static com.android.settings.fuelgauge.PowerUsageSummary.MENU_TOGGLE_APPS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.support.v7.preference.PreferenceScreen;
+import android.util.SparseArray;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+import com.android.settings.Utils;
+import com.android.settings.applications.LayoutPreference;
+import com.android.settings.fuelgauge.anomaly.Anomaly;
+import com.android.settings.fuelgauge.anomaly.AnomalyDetectionPolicy;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link PowerUsageSummary}.
+ */
+// TODO: Improve this test class so that it starts up the real activity and fragment.
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH,
+ sdk = TestConfig.SDK_VERSION,
+ shadows = {
+ SettingsShadowResources.class,
+ SettingsShadowResources.SettingsShadowTheme.class,
+ })
+public class PowerUsageSummaryTest {
+ private static final String STUB_STRING = "stub_string";
+ private static final int UID = 123;
+ private static final int UID_2 = 234;
+ private static final int POWER_MAH = 100;
+ private static final long TIME_SINCE_LAST_FULL_CHARGE_MS = 120 * 60 * 1000;
+ private static final long TIME_SINCE_LAST_FULL_CHARGE_US =
+ TIME_SINCE_LAST_FULL_CHARGE_MS * 1000;
+ private static final long USAGE_TIME_MS = 65 * 60 * 1000;
+ private static final double TOTAL_POWER = 200;
+ public static final String NEW_ML_EST_SUFFIX = "(New ML est)";
+ public static final String OLD_EST_SUFFIX = "(Old est)";
+ private static Intent sAdditionalBatteryInfoIntent;
+
+ @BeforeClass
+ public static void beforeClass() {
+ sAdditionalBatteryInfoIntent = new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
+ }
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mContext;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Menu mMenu;
+ @Mock
+ private MenuItem mToggleAppsMenu;
+ @Mock
+ private MenuItem mHighPowerMenu;
+ @Mock
+ private MenuInflater mMenuInflater;
+ @Mock
+ private BatterySipper mNormalBatterySipper;
+ @Mock
+ private BatterySipper mScreenBatterySipper;
+ @Mock
+ private BatterySipper mCellBatterySipper;
+ @Mock
+ private LayoutPreference mBatteryLayoutPref;
+ @Mock
+ private TextView mBatteryPercentText;
+ @Mock
+ private TextView mSummary1;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private BatteryStatsHelper mBatteryHelper;
+ @Mock
+ private PowerManager mPowerManager;
+ @Mock
+ private SettingsActivity mSettingsActivity;
+ @Mock
+ private LoaderManager mLoaderManager;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private AnomalyDetectionPolicy mAnomalyDetectionPolicy;
+ @Mock
+ private BatteryHeaderPreferenceController mBatteryHeaderPreferenceController;
+
+ private List<BatterySipper> mUsageList;
+ private Context mRealContext;
+ private TestFragment mFragment;
+ private FakeFeatureFactory mFeatureFactory;
+ private BatteryMeterView mBatteryMeterView;
+ private PowerGaugePreference mScreenUsagePref;
+ private PowerGaugePreference mLastFullChargePref;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mRealContext = RuntimeEnvironment.application;
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(mPowerManager);
+
+ mScreenUsagePref = new PowerGaugePreference(mRealContext);
+ mLastFullChargePref = new PowerGaugePreference(mRealContext);
+ mFragment = spy(new TestFragment(mContext));
+ mFragment.initFeatureProvider();
+ mBatteryMeterView = new BatteryMeterView(mRealContext);
+ mBatteryMeterView.mDrawable = new BatteryMeterView.BatteryMeterDrawable(mRealContext, 0);
+ doNothing().when(mFragment).restartBatteryStatsLoader();
+ doReturn(mock(LoaderManager.class)).when(mFragment).getLoaderManager();
+
+ when(mFragment.getActivity()).thenReturn(mSettingsActivity);
+ when(mToggleAppsMenu.getItemId()).thenReturn(MENU_TOGGLE_APPS);
+ when(mHighPowerMenu.getItemId()).thenReturn(MENU_HIGH_POWER_APPS);
+ when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
+ .thenReturn(sAdditionalBatteryInfoIntent);
+ when(mBatteryHelper.getTotalPower()).thenReturn(TOTAL_POWER);
+ when(mBatteryHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
+ TIME_SINCE_LAST_FULL_CHARGE_US);
+
+ when(mNormalBatterySipper.getUid()).thenReturn(UID);
+ mNormalBatterySipper.totalPowerMah = POWER_MAH;
+ mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
+
+ mCellBatterySipper.drainType = BatterySipper.DrainType.CELL;
+ mCellBatterySipper.totalPowerMah = POWER_MAH;
+
+ when(mBatteryLayoutPref.findViewById(R.id.summary1)).thenReturn(mSummary1);
+ when(mBatteryLayoutPref.findViewById(R.id.battery_percent)).thenReturn(mBatteryPercentText);
+ when(mBatteryLayoutPref.findViewById(R.id.battery_header_icon))
+ .thenReturn(mBatteryMeterView);
+ mFragment.setBatteryLayoutPreference(mBatteryLayoutPref);
+
+ mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
+ mScreenBatterySipper.usageTimeMs = USAGE_TIME_MS;
+
+ mUsageList = new ArrayList<>();
+ mUsageList.add(mNormalBatterySipper);
+ mUsageList.add(mScreenBatterySipper);
+ mUsageList.add(mCellBatterySipper);
+
+ mFragment.mStatsHelper = mBatteryHelper;
+ when(mBatteryHelper.getUsageList()).thenReturn(mUsageList);
+ mFragment.mScreenUsagePref = mScreenUsagePref;
+ mFragment.mLastFullChargePref = mLastFullChargePref;
+ mFragment.mBatteryUtils = spy(new BatteryUtils(mRealContext));
+ }
+
+ @Test
+ public void testOptionsMenu_menuHighPower_metricEventInvoked() {
+ mFragment.onOptionsItemSelected(mHighPowerMenu);
+
+ verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
+ MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_OPTIMIZATION);
+ }
+
+ @Test
+ public void testOptionsMenu_menuAppToggle_metricEventInvoked() {
+ mFragment.onOptionsItemSelected(mToggleAppsMenu);
+ mFragment.mShowAllApps = false;
+
+ verify(mFeatureFactory.metricsFeatureProvider).action(mContext,
+ MetricsProto.MetricsEvent.ACTION_SETTINGS_MENU_BATTERY_APPS_TOGGLE, true);
+ }
+
+ @Test
+ public void testOptionsMenu_toggleAppsEnabled() {
+ when(mFeatureFactory.powerUsageFeatureProvider.isPowerAccountingToggleEnabled())
+ .thenReturn(true);
+ mFragment.mShowAllApps = false;
+
+ mFragment.onCreateOptionsMenu(mMenu, mMenuInflater);
+
+ verify(mMenu).add(Menu.NONE, MENU_TOGGLE_APPS, Menu.NONE, R.string.show_all_apps);
+ }
+
+ @Test
+ public void testOptionsMenu_clickToggleAppsMenu_dataChanged() {
+ testToggleAllApps(true);
+ testToggleAllApps(false);
+ }
+
+ private void testToggleAllApps(final boolean isShowApps) {
+ mFragment.mShowAllApps = isShowApps;
+
+ mFragment.onOptionsItemSelected(mToggleAppsMenu);
+ assertThat(mFragment.mShowAllApps).isEqualTo(!isShowApps);
+ }
+
+ @Test
+ public void testFindBatterySipperByType_findTypeScreen() {
+ BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
+ BatterySipper.DrainType.SCREEN);
+
+ assertThat(sipper).isSameAs(mScreenBatterySipper);
+ }
+
+ @Test
+ public void testFindBatterySipperByType_findTypeApp() {
+ BatterySipper sipper = mFragment.findBatterySipperByType(mUsageList,
+ BatterySipper.DrainType.APP);
+
+ assertThat(sipper).isSameAs(mNormalBatterySipper);
+ }
+
+ @Test
+ public void testUpdateScreenPreference_showCorrectSummary() {
+ doReturn(mScreenBatterySipper).when(mFragment).findBatterySipperByType(any(), any());
+ doReturn(mRealContext).when(mFragment).getContext();
+ final CharSequence expectedSummary = Utils.formatElapsedTime(mRealContext, USAGE_TIME_MS,
+ false);
+
+ mFragment.updateScreenPreference();
+
+ assertThat(mScreenUsagePref.getSubtitle()).isEqualTo(expectedSummary);
+ }
+
+ @Test
+ public void testUpdateLastFullChargePreference_showCorrectSummary() {
+ doReturn(mRealContext).when(mFragment).getContext();
+
+ mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS);
+
+ assertThat(mLastFullChargePref.getSubtitle()).isEqualTo("2 hr. ago");
+ }
+
+ @Test
+ public void testUpdatePreference_usageListEmpty_shouldNotCrash() {
+ when(mBatteryHelper.getUsageList()).thenReturn(new ArrayList<BatterySipper>());
+ doReturn(STUB_STRING).when(mFragment).getString(anyInt(), any());
+ doReturn(mRealContext).when(mFragment).getContext();
+
+ // Should not crash when update
+ mFragment.updateScreenPreference();
+ }
+
+ @Test
+ public void testNonIndexableKeys_MatchPreferenceKeys() {
+ final Context context = RuntimeEnvironment.application;
+ final List<String> niks = PowerUsageSummary.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(context);
+
+ final List<String> keys = XmlTestUtils.getKeysFromPreferenceXml(context,
+ R.xml.power_usage_summary);
+
+ assertThat(keys).containsAllIn(niks);
+ }
+
+ @Test
+ public void testPreferenceControllers_getPreferenceKeys_existInPreferenceScreen() {
+ final Context context = RuntimeEnvironment.application;
+ final PowerUsageSummary fragment = new PowerUsageSummary();
+ final List<String> preferenceScreenKeys = XmlTestUtils.getKeysFromPreferenceXml(context,
+ fragment.getPreferenceScreenResId());
+ final List<String> preferenceKeys = new ArrayList<>();
+
+ for (AbstractPreferenceController controller : fragment.getPreferenceControllers(context)) {
+ preferenceKeys.add(controller.getPreferenceKey());
+ }
+
+ assertThat(preferenceScreenKeys).containsAllIn(preferenceKeys);
+ }
+
+ @Test
+ public void testUpdateAnomalySparseArray() {
+ mFragment.mAnomalySparseArray = new SparseArray<>();
+ final List<Anomaly> anomalies = new ArrayList<>();
+ final Anomaly anomaly1 = new Anomaly.Builder().setUid(UID).build();
+ final Anomaly anomaly2 = new Anomaly.Builder().setUid(UID).build();
+ final Anomaly anomaly3 = new Anomaly.Builder().setUid(UID_2).build();
+ anomalies.add(anomaly1);
+ anomalies.add(anomaly2);
+ anomalies.add(anomaly3);
+
+ mFragment.updateAnomalySparseArray(anomalies);
+
+ assertThat(mFragment.mAnomalySparseArray.get(UID)).containsExactly(anomaly1, anomaly2);
+ assertThat(mFragment.mAnomalySparseArray.get(UID_2)).containsExactly(anomaly3);
+ }
+
+ @Test
+ public void testInitAnomalyDetectionIfPossible_detectionEnabled_init() {
+ doReturn(mLoaderManager).when(mFragment).getLoaderManager();
+ doReturn(mAnomalyDetectionPolicy).when(mFragment).getAnomalyDetectionPolicy();
+ when(mAnomalyDetectionPolicy.isAnomalyDetectionEnabled()).thenReturn(true);
+
+ mFragment.restartAnomalyDetectionIfPossible();
+
+ verify(mLoaderManager).restartLoader(eq(PowerUsageSummary.ANOMALY_LOADER), eq(Bundle.EMPTY),
+ any());
+ }
+
+ @Test
+ public void testShowBothEstimates_summariesAreBothModified() {
+ doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary2);
+ doReturn(new TextView(mRealContext)).when(mBatteryLayoutPref).findViewById(R.id.summary1);
+ mFragment.onLongClick(new View(mRealContext));
+ TextView summary1 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary1);
+ TextView summary2 = mFragment.mBatteryLayoutPref.findViewById(R.id.summary2);
+ Robolectric.flushBackgroundThreadScheduler();
+ assertThat(summary2.getText().toString().contains(NEW_ML_EST_SUFFIX));
+ assertThat(summary1.getText().toString().contains(OLD_EST_SUFFIX));
+ }
+
+ @Test
+ public void testSaveInstanceState_showAllAppsRestored() {
+ Bundle bundle = new Bundle();
+ mFragment.mShowAllApps = true;
+ doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
+
+ mFragment.onSaveInstanceState(bundle);
+ mFragment.restoreSavedInstance(bundle);
+
+ assertThat(mFragment.mShowAllApps).isTrue();
+ }
+
+ @Test
+ public void testDebugMode() {
+ doReturn(true).when(mFeatureFactory.powerUsageFeatureProvider).isEstimateDebugEnabled();
+
+ mFragment.restartBatteryInfoLoader();
+ ArgumentCaptor<View.OnLongClickListener> listener = ArgumentCaptor.forClass(
+ View.OnLongClickListener.class);
+ verify(mSummary1).setOnLongClickListener(listener.capture());
+
+ // Calling the listener should disable it.
+ listener.getValue().onLongClick(mSummary1);
+ verify(mSummary1).setOnLongClickListener(null);
+
+ // Restarting the loader should reset the listener.
+ mFragment.restartBatteryInfoLoader();
+ verify(mSummary1, times(2)).setOnLongClickListener(any(View.OnLongClickListener.class));
+ }
+
+ @Test
+ public void testRestartBatteryStatsLoader_notClearHeader_quickUpdateNotInvoked() {
+ mFragment.mBatteryHeaderPreferenceController = mBatteryHeaderPreferenceController;
+
+ mFragment.restartBatteryStatsLoader(false /* clearHeader */);
+
+ verify(mBatteryHeaderPreferenceController, never()).quickUpdateHeaderPreference();
+ }
+
+ public static class TestFragment extends PowerUsageSummary {
+ private Context mContext;
+
+ public TestFragment(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+
+ @Override
+ protected void refreshUi() {
+ // Leave it empty for toggle apps menu test
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
index cdd1e9e..c18372c 100644
--- a/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/VisibilityPreferenceControllerTest.java
@@ -205,10 +205,11 @@
RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
mController.updateState(pref);
- ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ ArgumentCaptor<CharSequence[]> argumentCaptor =
+ ArgumentCaptor.forClass(CharSequence[].class);
verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
- assertFalse(Arrays.asList(argumentCaptor.getValue())
- .contains(VISIBILITY_NO_OVERRIDE));
+ assertFalse(toStringList(argumentCaptor.getValue())
+ .contains(String.valueOf(VISIBILITY_NO_OVERRIDE)));
}
@Test
@@ -223,10 +224,11 @@
RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
mController.updateState(pref);
- ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ ArgumentCaptor<CharSequence[]> argumentCaptor =
+ ArgumentCaptor.forClass(CharSequence[].class);
verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
- assertFalse(Arrays.asList(argumentCaptor.getValue())
- .contains(VISIBILITY_NO_OVERRIDE));
+ assertFalse(toStringList(argumentCaptor.getValue())
+ .contains(String.valueOf(VISIBILITY_NO_OVERRIDE)));
}
@Test
@@ -238,15 +240,24 @@
RestrictedDropDownPreference pref = mock(RestrictedDropDownPreference.class);
mController.updateState(pref);
- ArgumentCaptor<String[]> argumentCaptor = ArgumentCaptor.forClass(String[].class);
+ ArgumentCaptor<CharSequence[]> argumentCaptor =
+ ArgumentCaptor.forClass(CharSequence[].class);
verify(pref, times(1)).setEntryValues(argumentCaptor.capture());
- List<String> values = Arrays.asList(argumentCaptor.getValue());
+ List<String> values = toStringList(argumentCaptor.getValue());
assertEquals(3, values.size());
assertTrue(values.contains(String.valueOf(VISIBILITY_NO_OVERRIDE)));
assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_PRIVATE)));
assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_SECRET)));
}
+ private static List<String> toStringList(CharSequence[] charSequences) {
+ List<String> result = new ArrayList<>();
+ for (CharSequence charSequence : charSequences) {
+ result.add(charSequence.toString());
+ }
+ return result;
+ }
+
@Test
public void testUpdateState_noChannelOverride() throws Exception {
Settings.Secure.putInt(mContext.getContentResolver(),
diff --git a/tests/unit/Android.mk b/tests/unit/Android.mk
index 58fe7dd..cbf91db 100644
--- a/tests/unit/Android.mk
+++ b/tests/unit/Android.mk
@@ -5,14 +5,19 @@
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common
+LOCAL_JAVA_LIBRARIES := \
+ android.test.runner \
+ telephony-common \
+ ims-common \
+ android.test.base \
+ android.test.mock \
+
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
espresso-core \
espresso-contrib-nodep \
espresso-intents-nodep \
- legacy-android-test \
mockito-target-minus-junit4 \
platform-test-annotations \
truth-prebuilt \
diff --git a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
index 82f0e0a..f7e956b 100644
--- a/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
+++ b/tests/unit/src/com/android/settings/applications/ExternalSourcesSettingsTest.java
@@ -37,6 +37,7 @@
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
+import android.support.test.filters.Suppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.BySelector;
@@ -56,6 +57,7 @@
import java.util.List;
+@Suppress
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ExternalSourcesSettingsTest {
diff --git a/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java
index 69c8c54..fe8203c 100644
--- a/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/display/ThemePreferenceControllerTest.java
@@ -118,8 +118,8 @@
}
return info;
});
- PackageInfo pi = new PackageInfo();
- pi.isStaticOverlay = true;
+ PackageInfo pi = mock(PackageInfo.class);
+ when(pi.isStaticOverlayPackage()).thenReturn(true);
when(mMockPackageManager.getPackageInfo(eq("com.android.Theme1"), anyInt())).thenReturn(pi);
when(mMockPackageManager.getPackageInfo(eq("com.android.Theme2"), anyInt())).thenReturn(
new PackageInfo());