Merge "Hide edit menu if network is uneditable" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 30f181a..eeb9694 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -396,12 +396,15 @@
android:exported="true"
android:permission="android.permission.NETWORK_STACK"
android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight">
+ <!-- TODO: Consider removing below two intent filters.
+ It seems like below two intent filters can be removed because when the notification
+ is clicked, this activity will be launched anyway. -->
<intent-filter>
- <action android:name="android.net.conn.PROMPT_UNVALIDATED" />
+ <action android:name="android.net.action.PROMPT_UNVALIDATED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
- <action android:name="android.net.conn.PROMPT_LOST_VALIDATION" />
+ <action android:name="android.net.action.PROMPT_LOST_VALIDATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
@@ -3185,6 +3188,33 @@
android:value="com.android.settings.applications.appinfo.ManageExternalStorageDetails" />
</activity>
+ <activity
+ android:name="Settings$MediaManagementAppsActivity"
+ android:exported="true"
+ android:label="@string/media_management_apps_title">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.REQUEST_MANAGE_MEDIA" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.manageapplications.ManageApplications" />
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity>
+
+ <activity
+ android:name="Settings$AppMediaManagementAppsActivity"
+ android:exported="true"
+ android:label="@string/media_management_apps_title">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.REQUEST_MANAGE_MEDIA" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="package" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.applications.appinfo.MediaManagementAppsDetails" />
+ </activity>
+
<!-- Keep compatibility with old WebView-picker implementation -->
<activity-alias android:name=".WebViewImplementation"
android:targetActivity="Settings$WebViewAppPickerActivity"
diff --git a/res/drawable/accessibility_button_navigation.xml b/res/drawable/accessibility_button_navigation.xml
new file mode 100644
index 0000000..82e3c70
--- /dev/null
+++ b/res/drawable/accessibility_button_navigation.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <group>
+ <clip-path
+ android:pathData="M0,0h125v153h-125z"/>
+ <path
+ android:pathData="M7.4,-62.9L117.6,-62.9A6.3,6.3 0,0 1,123.9 -56.6L123.9,145.6A6.3,6.3 0,0 1,117.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#DADCE0"
+ android:strokeColor="#BDC1C6"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,-62.9L116.6,-62.9A6.3,6.3 0,0 1,122.9 -56.6L122.9,145.6A6.3,6.3 0,0 1,116.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"/>
+ <path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"
+ android:fillColor="#F8F9FA"/>
+ <group>
+ <clip-path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"/>
+ </group>
+ </group>
+ <path
+ android:pathData="M7,126H118V140.6C118,143.582 115.582,146 112.6,146H12.4C9.418,146 7,143.582 7,140.6V126Z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.87"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M63.5,138.688C64.713,138.688 65.697,137.708 65.697,136.5C65.697,135.292 64.713,134.312 63.5,134.312C62.286,134.312 61.303,135.292 61.303,136.5C61.303,137.708 62.286,138.688 63.5,138.688Z"
+ android:fillColor="#00000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#9AA0A6"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M33.694,133.953C33.827,133.876 33.994,133.972 33.994,134.126V138.874C33.994,139.028 33.827,139.125 33.694,139.047L29.604,136.673C29.471,136.596 29.471,136.404 29.604,136.327L33.694,133.953Z"
+ android:fillColor="#00000000"
+ android:fillType="evenOdd"
+ android:strokeColor="#9AA0A6"/>
+ <path
+ android:pathData="M96.111,131.2C96.111,131.86 95.611,132.4 95,132.4C94.389,132.4 93.889,131.86 93.889,131.2C93.889,130.54 94.389,130 95,130C95.611,130 96.111,130.54 96.111,131.2ZM95,133C96.572,133 98.272,132.82 99.722,132.4L100,133.6C98.967,133.9 97.778,134.098 96.667,134.2V142H95.556V138.4H94.444V142H93.333V134.2C92.222,134.098 91.033,133.9 90,133.6L90.278,132.4C91.728,132.82 93.428,133 95,133Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M94.5,135.5m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0"
+ android:strokeWidth="4"
+ android:fillColor="#00000000"
+ android:strokeColor="#4285F4"/>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_button_preview_base.xml b/res/drawable/accessibility_button_preview_base.xml
new file mode 100644
index 0000000..9e3ec59
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_base.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <group>
+ <clip-path
+ android:pathData="M0,0h125v153h-125z"/>
+ <path
+ android:pathData="M7.4,-62.9L117.6,-62.9A6.3,6.3 0,0 1,123.9 -56.6L123.9,145.6A6.3,6.3 0,0 1,117.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#DADCE0"
+ android:strokeColor="#BDC1C6"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,-62.9L116.6,-62.9A6.3,6.3 0,0 1,122.9 -56.6L122.9,145.6A6.3,6.3 0,0 1,116.6 151.9L7.4,151.9A6.3,6.3 0,0 1,1.1 145.6L1.1,-56.6A6.3,6.3 0,0 1,7.4 -62.9z"/>
+ <path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"
+ android:fillColor="#F8F9FA"/>
+ <group>
+ <clip-path
+ android:pathData="M12.4,-61L112.6,-61A5.4,5.4 0,0 1,118 -55.6L118,140.6A5.4,5.4 0,0 1,112.6 146L12.4,146A5.4,5.4 0,0 1,7 140.6L7,-55.6A5.4,5.4 0,0 1,12.4 -61z"/>
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_button_preview_large_floating_menu.xml b/res/drawable/accessibility_button_preview_large_floating_menu.xml
new file mode 100644
index 0000000..e003dc7
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_large_floating_menu.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <path
+ android:pathData="M0,0h125v153h-125z"
+ android:fillColor="#00000000"/>
+ <group>
+ <clip-path
+ android:pathData="M89,95h29v34h-29z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M105,97.5L131,97.5A14.5,14.5 0,0 1,145.5 112L145.5,112A14.5,14.5 0,0 1,131 126.5L105,126.5A14.5,14.5 0,0 1,90.5 112L90.5,112A14.5,14.5 0,0 1,105 97.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M105.4,112m-11.2,0a11.2,11.2 0,1 1,22.4 0a11.2,11.2 0,1 1,-22.4 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M106.467,107.733C106.467,108.32 105.987,108.8 105.4,108.8C104.814,108.8 104.334,108.32 104.334,107.733C104.334,107.147 104.814,106.667 105.4,106.667C105.987,106.667 106.467,107.147 106.467,107.733ZM105.4,109.333C106.91,109.333 108.542,109.173 109.934,108.8L110.2,109.867C109.208,110.133 108.067,110.309 107,110.4V117.333H105.934V114.133H104.867V117.333H103.8V110.4C102.734,110.309 101.592,110.133 100.6,109.867L100.867,108.8C102.259,109.173 103.891,109.333 105.4,109.333Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/accessibility_button_preview_small_floating_menu.xml b/res/drawable/accessibility_button_preview_small_floating_menu.xml
new file mode 100644
index 0000000..3ff8e4b
--- /dev/null
+++ b/res/drawable/accessibility_button_preview_small_floating_menu.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="125dp"
+ android:height="153dp"
+ android:viewportWidth="125"
+ android:viewportHeight="153">
+ <path
+ android:pathData="M0,0h125v153h-125z"
+ android:fillColor="#00000000"/>
+ <group>
+ <clip-path
+ android:pathData="M89,106h29v22h-29z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M111,107.5L137,107.5A9.5,9.5 0,0 1,146.5 117L146.5,117A9.5,9.5 0,0 1,137 126.5L111,126.5A9.5,9.5 0,0 1,101.5 117L101.5,117A9.5,9.5 0,0 1,111 107.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M111.168,116.968m-7.168,0a7.168,7.168 0,1 1,14.336 0a7.168,7.168 0,1 1,-14.336 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M111.851,114.237C111.851,114.612 111.543,114.92 111.168,114.92C110.792,114.92 110.485,114.612 110.485,114.237C110.485,113.861 110.792,113.554 111.168,113.554C111.543,113.554 111.851,113.861 111.851,114.237ZM111.168,115.261C112.134,115.261 113.178,115.158 114.069,114.92L114.24,115.602C113.605,115.773 112.875,115.886 112.192,115.944V120.381H111.509V118.333H110.827V120.381H110.144V115.944C109.461,115.886 108.731,115.773 108.096,115.602L108.267,114.92C109.157,115.158 110.202,115.261 111.168,115.261Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/accessibility_magnification_full_screen.png b/res/drawable/accessibility_magnification_full_screen.png
deleted file mode 100644
index 2e87ab8..0000000
--- a/res/drawable/accessibility_magnification_full_screen.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/accessibility_magnification_full_screen.xml b/res/drawable/accessibility_magnification_full_screen.xml
new file mode 100644
index 0000000..09d1a7e
--- /dev/null
+++ b/res/drawable/accessibility_magnification_full_screen.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="77dp"
+ android:height="134dp"
+ android:viewportWidth="77"
+ android:viewportHeight="134">
+ <path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"
+ android:strokeWidth="1.8"
+ android:fillColor="#F2F3F4"
+ android:strokeColor="#DADCE0"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"/>
+ <path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"
+ android:fillColor="#ffffff"/>
+ <group>
+ <clip-path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"/>
+ <path
+ android:pathData="M13,5L65,5A8,8 0,0 1,73 13L73,120A8,8 0,0 1,65 128L13,128A8,8 0,0 1,5 120L5,13A8,8 0,0 1,13 5z"
+ android:strokeLineJoin="bevel"
+ android:strokeWidth="10"
+ android:fillColor="#00000000"
+ android:strokeColor="#F29900"/>
+ <path
+ android:pathData="M51.077,14V18.314H56.612L50,24.958L53.037,28L59.692,21.334V26.921H64V14H51.077Z"
+ android:fillColor="#F29900"/>
+ <path
+ android:pathData="M25.963,104L19.308,110.655V105.077H15V118H27.923V113.692H22.366L29,107.037L25.963,104Z"
+ android:fillColor="#F29900"/>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_magnification_switch.xml b/res/drawable/accessibility_magnification_switch.xml
new file mode 100644
index 0000000..21e0cef
--- /dev/null
+++ b/res/drawable/accessibility_magnification_switch.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="77dp"
+ android:height="134dp"
+ android:viewportWidth="77"
+ android:viewportHeight="134">
+ <path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"
+ android:strokeWidth="1.8"
+ android:fillColor="#F2F3F4"
+ android:strokeColor="#DADCE0"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"/>
+ <path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"
+ android:fillColor="#ffffff"/>
+ <group>
+ <clip-path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"/>
+ <path
+ android:pathData="M39,94h24v24h-24z"
+ android:fillColor="#000000"
+ android:fillAlpha="0.7"/>
+ <path
+ android:pathData="M51.414,98.138H45.138C44.033,98.138 43.138,99.033 43.138,100.138V112.689C43.138,113.794 44.033,114.689 45.138,114.689H57.69C58.794,114.689 59.69,113.794 59.69,112.689L59.69,106.414H57.69L57.69,112.689L45.138,112.689V100.138H51.414V98.138ZM49.414,108.414H48.448V109.379H49.414V108.414ZM48.448,106.414H46.448V108.414V109.379V111.379H48.448H49.414H51.414V109.379V108.414V106.414H49.414H48.448ZM55.891,103.103L58.035,103.103V104.758L53.069,104.758V99.793L54.724,99.793V101.936L58.275,98.378L59.45,99.553L55.891,103.103Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M13,5L65,5A8,8 0,0 1,73 13L73,120A8,8 0,0 1,65 128L13,128A8,8 0,0 1,5 120L5,13A8,8 0,0 1,13 5z"
+ android:strokeLineJoin="bevel"
+ android:strokeWidth="10"
+ android:fillColor="#00000000"
+ android:strokeColor="#F29900"/>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_magnification_window_screen.png b/res/drawable/accessibility_magnification_window_screen.png
deleted file mode 100644
index a7f2a25..0000000
--- a/res/drawable/accessibility_magnification_window_screen.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/accessibility_magnification_window_screen.xml b/res/drawable/accessibility_magnification_window_screen.xml
new file mode 100644
index 0000000..d7e164c
--- /dev/null
+++ b/res/drawable/accessibility_magnification_window_screen.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="77dp"
+ android:height="134dp"
+ android:viewportWidth="77"
+ android:viewportHeight="134">
+ <path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"
+ android:strokeWidth="1.8"
+ android:fillColor="#F2F3F4"
+ android:strokeColor="#DADCE0"/>
+ <group>
+ <clip-path
+ android:pathData="M7.4,1.1L69.6,1.1A6.3,6.3 0,0 1,75.9 7.4L75.9,126.6A6.3,6.3 0,0 1,69.6 132.9L7.4,132.9A6.3,6.3 0,0 1,1.1 126.6L1.1,7.4A6.3,6.3 0,0 1,7.4 1.1z"/>
+ <path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"
+ android:fillColor="#ffffff"/>
+ <group>
+ <clip-path
+ android:pathData="M10.442,4.948L67.167,4.948A5.4,5.4 0,0 1,72.567 10.348L72.567,123.548A5.4,5.4 0,0 1,67.167 128.948L10.442,128.948A5.4,5.4 0,0 1,5.042 123.548L5.042,10.348A5.4,5.4 0,0 1,10.442 4.948z"/>
+ <path
+ android:pathData="M15,47L64,47A2,2 0,0 1,66 49L66,84A2,2 0,0 1,64 86L15,86A2,2 0,0 1,13 84L13,49A2,2 0,0 1,15 47z"
+ android:strokeLineJoin="bevel"
+ android:strokeWidth="5"
+ android:fillColor="#00000000"
+ android:strokeColor="#F29900"/>
+ <path
+ android:pathData="M47.077,53V57.314H52.612L46,63.958L49.037,67L55.692,60.334V65.921H60V53H47.077Z"
+ android:fillColor="#F29900"/>
+ <path
+ android:pathData="M29.963,66L23.308,72.655V67.077H19V80H31.923V75.692H26.366L33,69.037L29.963,66Z"
+ android:fillColor="#F29900"/>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/accessibility_shortcut_type_software_floating.xml b/res/drawable/accessibility_shortcut_type_software_floating.xml
new file mode 100644
index 0000000..9582015
--- /dev/null
+++ b/res/drawable/accessibility_shortcut_type_software_floating.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="180dp"
+ android:height="180dp"
+ android:viewportWidth="180"
+ android:viewportHeight="180">
+ <path
+ android:pathData="M90,90m-89,0a89,89 0,1 1,178 0a89,89 0,1 1,-178 0"
+ android:strokeWidth="2"
+ android:fillColor="#00000000"
+ android:strokeColor="#ECEEEF"/>
+ <group>
+ <clip-path
+ android:pathData="M90,90m-87,0a87,87 0,1 1,174 0a87,87 0,1 1,-174 0"/>
+ <path
+ android:pathData="M35.4,-70.9L144.6,-70.9A6.3,6.3 0,0 1,150.9 -64.6L150.9,137.6A6.3,6.3 0,0 1,144.6 143.9L35.4,143.9A6.3,6.3 0,0 1,29.1 137.6L29.1,-64.6A6.3,6.3 0,0 1,35.4 -70.9z"
+ android:strokeWidth="1.8"
+ android:fillColor="#F2F3F4"
+ android:strokeColor="#DADCE0"/>
+ <group>
+ <clip-path
+ android:pathData="M35.4,-70.9L144.6,-70.9A6.3,6.3 0,0 1,150.9 -64.6L150.9,137.6A6.3,6.3 0,0 1,144.6 143.9L35.4,143.9A6.3,6.3 0,0 1,29.1 137.6L29.1,-64.6A6.3,6.3 0,0 1,35.4 -70.9z"/>
+ <path
+ android:pathData="M40.4,-69L140.6,-69A5.4,5.4 0,0 1,146 -63.6L146,132.6A5.4,5.4 0,0 1,140.6 138L40.4,138A5.4,5.4 0,0 1,35 132.6L35,-63.6A5.4,5.4 0,0 1,40.4 -69z"
+ android:fillColor="#ffffff"/>
+ <group>
+ <clip-path
+ android:pathData="M40.4,-69L140.6,-69A5.4,5.4 0,0 1,146 -63.6L146,132.6A5.4,5.4 0,0 1,140.6 138L40.4,138A5.4,5.4 0,0 1,35 132.6L35,-63.6A5.4,5.4 0,0 1,40.4 -69z"/>
+ <path
+ android:strokeWidth="1"
+ android:pathData="M132,90.5L158,90.5A14.5,14.5 0,0 1,172.5 105L172.5,105A14.5,14.5 0,0 1,158 119.5L132,119.5A14.5,14.5 0,0 1,117.5 105L117.5,105A14.5,14.5 0,0 1,132 90.5z"
+ android:fillColor="#ffffff"
+ android:strokeColor="#DADCE0"/>
+ <path
+ android:pathData="M132.4,105m-11.2,0a11.2,11.2 0,1 1,22.4 0a11.2,11.2 0,1 1,-22.4 0"
+ android:fillColor="#80868B"/>
+ <path
+ android:pathData="M133.467,100.733C133.467,101.32 132.987,101.8 132.4,101.8C131.813,101.8 131.333,101.32 131.333,100.733C131.333,100.147 131.813,99.666 132.4,99.666C132.987,99.666 133.467,100.147 133.467,100.733ZM132.4,102.333C133.909,102.333 135.541,102.173 136.933,101.8L137.2,102.867C136.208,103.133 135.067,103.309 134,103.4V110.333H132.933V107.133H131.867V110.333H130.8V103.4C129.733,103.309 128.592,103.133 127.6,102.867L127.867,101.8C129.259,102.173 130.891,102.333 132.4,102.333Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M121.719,120.653C121.719,121.29 121.198,121.81 120.562,121.81C119.927,121.81 119.406,121.29 119.406,120.653C119.406,120.017 119.927,119.497 120.562,119.497C121.198,119.497 121.719,120.017 121.719,120.653ZM120.562,122.533C122.38,122.533 124.346,122.316 126.023,121.81L126.344,123.255C125.149,123.617 123.774,123.855 122.49,123.978V133.374H121.205V129.038H119.92V133.374H118.635V123.978C117.351,123.855 115.976,123.617 114.781,123.255L115.102,121.81C116.779,122.316 118.745,122.533 120.562,122.533Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <group>
+ <clip-path
+ android:pathData="M121.719,120.653C121.719,121.29 121.198,121.81 120.562,121.81C119.927,121.81 119.406,121.29 119.406,120.653C119.406,120.017 119.927,119.497 120.562,119.497C121.198,119.497 121.719,120.017 121.719,120.653ZM120.562,122.533C122.38,122.533 124.346,122.316 126.023,121.81L126.344,123.255C125.149,123.617 123.774,123.855 122.49,123.978V133.374H121.205V129.038H119.92V133.374H118.635V123.978C117.351,123.855 115.976,123.617 114.781,123.255L115.102,121.81C116.779,122.316 118.745,122.533 120.562,122.533Z"
+ android:fillType="evenOdd"/>
+ </group>
+ </group>
+ </group>
+ </group>
+</vector>
diff --git a/res/drawable/ic_no_internet_airplane.xml b/res/drawable/ic_no_internet_airplane.xml
new file mode 100644
index 0000000..3b22811
--- /dev/null
+++ b/res/drawable/ic_no_internet_airplane.xml
@@ -0,0 +1,28 @@
+<!--
+ Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10c0.34,0 0.68,-0.02 1.01,-0.05V20h-1v-0.04c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96H13v-2H9.66c-0.09,-0.66 -0.16,-1.32 -0.16,-2s0.07,-1.35 0.16,-2H21.8C20.87,5.44 16.83,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56C16.43,5.07 17.96,6.35 18.92,8zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82C10.52,6.57 11.17,5.24 12,4.04zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2s0.06,1.34 0.14,2H4.26zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56C7.57,18.93 6.04,17.66 5.08,16zM8.03,8H5.08c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8z"
+ android:fillAlpha="0.3"/>
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,19.3v-0.9l-3.37,-2.25v-2.47C18.63,13.3 18.35,13 18,13s-0.63,0.3 -0.63,0.68v2.47L14,18.4v0.9l3.37,-1.12v2.48l-0.84,0.68V22L18,21.55L19.47,22v-0.67l-0.84,-0.68v-2.48L22,19.3z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_no_internet_unavailable.xml b/res/drawable/ic_no_internet_unavailable.xml
deleted file mode 100644
index d255cb4..0000000
--- a/res/drawable/ic_no_internet_unavailable.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
- Copyright (C) 2021 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 xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M2,12C2,6.48 6.47,2 11.99,2C17.52,2 22,6.48 22,12c0,0.34 -0.02,0.67 -0.05,1h-2.02c0.04,-0.33 0.07,-0.66 0.07,-1c0,-0.69 -0.1,-1.36 -0.26,-2h-3.38c0.08,0.66 0.14,1.32 0.14,2c0,0.34 -0.01,0.67 -0.04,1h-2.01c0.03,-0.33 0.05,-0.66 0.05,-1c0,-0.68 -0.07,-1.35 -0.16,-2H9.66c-0.09,0.65 -0.16,1.32 -0.16,2s0.07,1.34 0.16,2H13v2h-2.91c0.43,1.43 1.08,2.76 1.91,3.96V20h1v1.95C12.67,21.98 12.33,22 11.99,22C6.47,22 2,17.52 2,12zM15.97,8h2.95c-0.96,-1.65 -2.49,-2.93 -4.33,-3.56C15.19,5.55 15.65,6.75 15.97,8zM13.91,8C13.48,6.57 12.83,5.24 12,4.04c-0.83,1.2 -1.48,2.53 -1.91,3.96H13.91zM4,12c0,0.69 0.1,1.36 0.26,2h3.38c-0.08,-0.66 -0.14,-1.32 -0.14,-2s0.06,-1.34 0.14,-2H4.26C4.1,10.64 4,11.31 4,12zM8.03,16H5.08c0.96,1.66 2.49,2.93 4.33,3.56C8.81,18.45 8.35,17.25 8.03,16zM5.08,8h2.95c0.32,-1.25 0.78,-2.45 1.38,-3.56C7.57,5.07 6.04,6.34 5.08,8z"
- android:fillAlpha="0.3"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
-</vector>
\ No newline at end of file
diff --git a/res/layout/accessibility_button_preview.xml b/res/layout/accessibility_button_preview.xml
new file mode 100644
index 0000000..07cb0ff
--- /dev/null
+++ b/res/layout/accessibility_button_preview.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:importantForAccessibility="noHideDescendants">
+
+ <ImageView
+ android:id="@+id/preview_image"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/accessibility_button_preview_height"
+ android:layout_gravity="center"
+ android:scaleType="fitCenter"
+ android:focusable="false"
+ android:clickable="false"
+ android:adjustViewBounds="true"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/accessibility_edit_magnification_mode.xml b/res/layout/accessibility_edit_magnification_mode.xml
deleted file mode 100644
index e4f3132..0000000
--- a/res/layout/accessibility_edit_magnification_mode.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License
- -->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/container_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scrollbarStyle="outsideOverlay">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="24dp">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/accessibility_magnification_area_settings_message"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary"
- android:layout_marginBottom="24dp"/>
-
- <include
- android:id="@+id/magnify_full_screen"
- layout="@layout/accessibility_edit_shortcut_component"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="32dp" />
-
- <include
- android:id="@+id/magnify_window_screen"
- layout="@layout/accessibility_edit_shortcut_component" />
-
- </LinearLayout>
-
-</ScrollView>
diff --git a/res/layout/accessibility_edit_shortcut_component.xml b/res/layout/accessibility_edit_shortcut_component.xml
index 0ccc88d..0d3324f 100644
--- a/res/layout/accessibility_edit_shortcut_component.xml
+++ b/res/layout/accessibility_edit_shortcut_component.xml
@@ -55,8 +55,8 @@
<ImageView
android:id="@+id/image"
- android:layout_width="176dp"
- android:layout_height="176dp"
+ android:layout_width="@dimen/accessibility_imageview_size"
+ android:layout_height="@dimen/accessibility_imageview_size"
android:layout_marginStart="44dp"
android:scaleType="fitCenter" />
diff --git a/res/layout/accessibility_magnification_mode_header.xml b/res/layout/accessibility_magnification_mode_header.xml
new file mode 100644
index 0000000..e476553
--- /dev/null
+++ b/res/layout/accessibility_magnification_mode_header.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2021 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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="?android:attr/dialogPreferredPadding">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/accessibility_magnification_area_settings_message"
+ android:textSize="16sp"
+ style="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorAlertDialogListItem"/>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/dialog_single_radio_choice_list_item.xml b/res/layout/dialog_single_radio_choice_list_item.xml
new file mode 100644
index 0000000..5842528
--- /dev/null
+++ b/res/layout/dialog_single_radio_choice_list_item.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+
+<com.android.settings.widget.CheckableRelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingBottom="12dp"
+ android:paddingTop="12dp">
+
+ <RadioButton
+ android:id="@+id/radioButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:clickable="false"
+ android:focusable="false" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@id/radioButton"
+ android:layout_marginStart="8dp"
+ android:layout_toEndOf="@+id/radioButton"
+ android:gravity="center_vertical|start"
+ style="?android:attr/textAppearanceMedium"
+ android:textSize="16sp" />
+
+ <TextView
+ android:id="@+id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:layout_marginTop="8dp"
+ android:layout_alignLeft="@id/title"
+ android:layout_below="@id/title" />
+
+ <ImageView
+ android:id="@+id/image"
+ android:layout_width="@dimen/accessibility_imageview_size"
+ android:layout_height="@dimen/accessibility_imageview_size"
+ android:layout_marginTop="16dp"
+ android:scaleType="fitStart"
+ android:layout_alignLeft="@id/title"
+ android:layout_below="@id/summary"/>
+</com.android.settings.widget.CheckableRelativeLayout>
\ No newline at end of file
diff --git a/res/layout/preference_labeled_continuous_slider.xml b/res/layout/preference_labeled_continuous_slider.xml
deleted file mode 100644
index 00e8796..0000000
--- a/res/layout/preference_labeled_continuous_slider.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingStart="56dp"
- android:paddingEnd="8dp"
- android:paddingTop="16dp"
- android:paddingBottom="16dp">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingBottom="16dp"
- android:layout_marginStart="16dp"
- android:maxLines="1"
- android:textAppearance="@android:style/TextAppearance.Material.Subhead"
- android:textColor="?android:attr/textColorPrimary" />
-
- <SeekBar
- android:id="@*android:id/seekbar"
- android:layout_gravity="center_vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- android:orientation="horizontal">
-
- <TextView
- android:id="@android:id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="16dp"
- android:layout_weight="1"
- android:maxLines="1"
- android:textAlignment="viewStart"/>
-
- <TextView
- android:id="@android:id/text2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="16dp"
- android:layout_weight="1"
- android:maxLines="1"
- android:textAlignment="viewEnd"/>
- </LinearLayout>
-</LinearLayout>
diff --git a/res/menu/storage_volume.xml b/res/menu/storage_volume.xml
index bf9f985..87f7515 100644
--- a/res/menu/storage_volume.xml
+++ b/res/menu/storage_volume.xml
@@ -28,9 +28,21 @@
android:id="@+id/storage_format"
android:title="@string/storage_menu_format" />
<item
+ android:id="@+id/storage_format_as_portable"
+ android:title="@string/storage_menu_format_public"
+ android:visible="false" />
+ <item
+ android:id="@+id/storage_format_as_internal"
+ android:title="@string/storage_menu_format_private"
+ android:visible="false" />
+ <item
android:id="@+id/storage_migrate"
android:title="@string/storage_menu_migrate" />
<item
android:id="@+id/storage_free"
android:title="@string/storage_menu_free" />
+ <item
+ android:id="@+id/storage_forget"
+ android:title="@string/storage_menu_forget"
+ android:visible="false" />
</menu>
diff --git a/res/values-af/arrays.xml b/res/values-af/arrays.xml
index b2d5ff1..53e1730 100644
--- a/res/values-af/arrays.xml
+++ b/res/values-af/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Geel op blou"</item>
<item msgid="747238414788976867">"Gepasmaak"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Sweef oor ander programme"</item>
+ <item msgid="3605616699204153590">"Navigasiebalk"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Klein"</item>
+ <item msgid="1666628329913333563">"Groot"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN met voorafgedeelde sleutels"</item>
diff --git a/res/values-am/arrays.xml b/res/values-am/arrays.xml
index b51723b..8e67eff 100644
--- a/res/values-am/arrays.xml
+++ b/res/values-am/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ቢጫ በሰማያዊ ላይ"</item>
<item msgid="747238414788976867">"ብጁ"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"በሌሎች መተግበሪያዎች ላይ በመንሳፈፍ ላይበሪያዎች ላይ መቀያየር"</item>
+ <item msgid="3605616699204153590">"የአሰሳ አሞሌ"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ትንሽ"</item>
+ <item msgid="1666628329913333563">"ትልቅ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN ከቅድመ-ተጋሪ ቁልፎች ጋር"</item>
diff --git a/res/values-ar/arrays.xml b/res/values-ar/arrays.xml
index e5082bf..d802340 100644
--- a/res/values-ar/arrays.xml
+++ b/res/values-ar/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"أصفر في أزرق"</item>
<item msgid="747238414788976867">"مخصص"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"عائم فوق التطبيقات الأخرى"</item>
+ <item msgid="3605616699204153590">"شريط التنقل"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"صغير"</item>
+ <item msgid="1666628329913333563">"كبير"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"شبكة افتراضية خاصة (VPN) عبر PPTP"</item>
<item msgid="2552427673212085780">"شبكة افتراضية خاصة (VPN) لـ L2TP/IPSec مزودة بمفاتيح مشتركة مسبقًا"</item>
diff --git a/res/values-as/arrays.xml b/res/values-as/arrays.xml
index ff25aa7..c9cc034 100644
--- a/res/values-as/arrays.xml
+++ b/res/values-as/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"নীলাৰ ওপৰত হালধীয়া"</item>
<item msgid="747238414788976867">"নিজৰ উপযোগিতা অনুযায়ী তৈয়াৰ কৰা"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"অন্য এপৰ ওপৰত ওপঙি আছে"</item>
+ <item msgid="3605616699204153590">"নেভিগেশ্বন বাৰ"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"সৰু"</item>
+ <item msgid="1666628329913333563">"ডাঙৰ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"পিপিটিপি ভিপিএন"</item>
<item msgid="2552427673212085780">"পূৰ্বে ভাগ-বতৰা কৰা কীসমূহৰ সৈতে L2TP/IPSec ভিপিএন"</item>
diff --git a/res/values-az/arrays.xml b/res/values-az/arrays.xml
index 2f6a67a..41c78a6 100644
--- a/res/values-az/arrays.xml
+++ b/res/values-az/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Mavi üstündə sarı"</item>
<item msgid="747238414788976867">"Fərd"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Digər tətbiqlərin üzərində üzür"</item>
+ <item msgid="3605616699204153590">"Naviqasiya paneli"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kiçik"</item>
+ <item msgid="1666628329913333563">"Böyük"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Öncədən paylaşılmış açarlar ilə L2TP/IPSec VPN"</item>
diff --git a/res/values-b+sr+Latn/arrays.xml b/res/values-b+sr+Latn/arrays.xml
index 8b6de00..52c60b6 100644
--- a/res/values-b+sr+Latn/arrays.xml
+++ b/res/values-b+sr+Latn/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Žuto na plavo"</item>
<item msgid="747238414788976867">"Prilagođeno"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Pluta preko drugih aplikacija"</item>
+ <item msgid="3605616699204153590">"Traka za navigaciju"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Mala"</item>
+ <item msgid="1666628329913333563">"Velika"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN sa unapred deljenim ključevima"</item>
diff --git a/res/values-be/arrays.xml b/res/values-be/arrays.xml
index 090b0f7..e96e881 100644
--- a/res/values-be/arrays.xml
+++ b/res/values-be/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Жоўты на сінім"</item>
<item msgid="747238414788976867">"Карыстальніцкі"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Плаваючая кнопка паверх праграм"</item>
+ <item msgid="3605616699204153590">"Панэль навігацыі"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Маленькая"</item>
+ <item msgid="1666628329913333563">"Вялікая"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN з загадзя размеркаванымі ключамі"</item>
diff --git a/res/values-bg/arrays.xml b/res/values-bg/arrays.xml
index baf8536..7ab3d37 100644
--- a/res/values-bg/arrays.xml
+++ b/res/values-bg/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Жълто върху синьо"</item>
<item msgid="747238414788976867">"Персонализиран"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Плаващ елемент над други приложения"</item>
+ <item msgid="3605616699204153590">"Лента за навигация"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Малък"</item>
+ <item msgid="1666628329913333563">"Голям"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN с PPTP"</item>
<item msgid="2552427673212085780">"VPN с L2TP/IPSec с предварително споделени ключове"</item>
diff --git a/res/values-bn/arrays.xml b/res/values-bn/arrays.xml
index 7a0afc9..b97e0b5 100644
--- a/res/values-bn/arrays.xml
+++ b/res/values-bn/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"নীলের উপর হলুদ"</item>
<item msgid="747238414788976867">"কাস্টম"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"মাল্টিটাস্কিং করতে অন্যান্য অ্যাপের উপরে ভেসে থাকা"</item>
+ <item msgid="3605616699204153590">"নেভিগেশন বার"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ছোট"</item>
+ <item msgid="1666628329913333563">"বড়"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"আগে থেকে শেয়ার করা কীগুলির সাথে L2TP/IPSec VPN"</item>
diff --git a/res/values-bs/arrays.xml b/res/values-bs/arrays.xml
index 2c095f5..0ce7093 100644
--- a/res/values-bs/arrays.xml
+++ b/res/values-bs/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Žuto na plavom"</item>
<item msgid="747238414788976867">"Prilagođeno"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Plutanje preko drugih aplikacija"</item>
+ <item msgid="3605616699204153590">"Navigaciona traka"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Malo"</item>
+ <item msgid="1666628329913333563">"Veliko"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN sa pre-shared lozinkama"</item>
diff --git a/res/values-ca/arrays.xml b/res/values-ca/arrays.xml
index b73e650..9f062ca 100644
--- a/res/values-ca/arrays.xml
+++ b/res/values-ca/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Groc sobre blau"</item>
<item msgid="747238414788976867">"Personalitzat"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flotant sobre altres aplicacions"</item>
+ <item msgid="3605616699204153590">"Barra de navegació"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Petit"</item>
+ <item msgid="1666628329913333563">"Gran"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec amb claus prèviament compartides"</item>
diff --git a/res/values-cs/arrays.xml b/res/values-cs/arrays.xml
index 0a8a790..625f457 100644
--- a/res/values-cs/arrays.xml
+++ b/res/values-cs/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Žluté na modrém"</item>
<item msgid="747238414788976867">"Vlastní"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Plovoucí přes ostatní aplikace"</item>
+ <item msgid="3605616699204153590">"Navigační panel"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Malé"</item>
+ <item msgid="1666628329913333563">"Velké"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN s protokolem PPTP"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN s předsdílenými klíči"</item>
diff --git a/res/values-da/arrays.xml b/res/values-da/arrays.xml
index 7d7b686..6cac514 100644
--- a/res/values-da/arrays.xml
+++ b/res/values-da/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Gult på blåt"</item>
<item msgid="747238414788976867">"Tilpasset"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Vises over andre apps"</item>
+ <item msgid="3605616699204153590">"Navigationslinje"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Lille"</item>
+ <item msgid="1666628329913333563">"Stor"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP-VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPsec VPN med forhåndsdelte nøgler"</item>
diff --git a/res/values-de/arrays.xml b/res/values-de/arrays.xml
index 18358ac..aeaee4a 100644
--- a/res/values-de/arrays.xml
+++ b/res/values-de/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Gelb auf Blau"</item>
<item msgid="747238414788976867">"Personalisiert"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Unverankert über anderen Apps"</item>
+ <item msgid="3605616699204153590">"Navigationsleiste"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Klein"</item>
+ <item msgid="1666628329913333563">"Groß"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP-VPN"</item>
<item msgid="2552427673212085780">"L2TP-/IPSec-VPN mit vorinstallierten Schlüsseln"</item>
diff --git a/res/values-el/arrays.xml b/res/values-el/arrays.xml
index d5f9fab..6532ab6 100644
--- a/res/values-el/arrays.xml
+++ b/res/values-el/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Κίτρινο σε μπλε"</item>
<item msgid="747238414788976867">"Προσαρμογή"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Κινούμενο πάνω από άλλες εφαρμογές"</item>
+ <item msgid="3605616699204153590">"Γραμμή πλοήγησης"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Μικρό"</item>
+ <item msgid="1666628329913333563">"Μεγάλο"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN με κλειδιά μοιρασμένα εκ των προτέρων"</item>
diff --git a/res/values-en-rAU/arrays.xml b/res/values-en-rAU/arrays.xml
index abf2d74..196868c 100644
--- a/res/values-en-rAU/arrays.xml
+++ b/res/values-en-rAU/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Yellow on blue"</item>
<item msgid="747238414788976867">"Customise"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Floating over other apps"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Small"</item>
+ <item msgid="1666628329913333563">"Large"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN with preshared keys"</item>
diff --git a/res/values-en-rCA/arrays.xml b/res/values-en-rCA/arrays.xml
index 54395da..015530f 100644
--- a/res/values-en-rCA/arrays.xml
+++ b/res/values-en-rCA/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Yellow on blue"</item>
<item msgid="747238414788976867">"Customise"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Floating over other apps"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Small"</item>
+ <item msgid="1666628329913333563">"Large"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN with preshared keys"</item>
diff --git a/res/values-en-rGB/arrays.xml b/res/values-en-rGB/arrays.xml
index abf2d74..196868c 100644
--- a/res/values-en-rGB/arrays.xml
+++ b/res/values-en-rGB/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Yellow on blue"</item>
<item msgid="747238414788976867">"Customise"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Floating over other apps"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Small"</item>
+ <item msgid="1666628329913333563">"Large"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN with preshared keys"</item>
diff --git a/res/values-en-rIN/arrays.xml b/res/values-en-rIN/arrays.xml
index abf2d74..196868c 100644
--- a/res/values-en-rIN/arrays.xml
+++ b/res/values-en-rIN/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Yellow on blue"</item>
<item msgid="747238414788976867">"Customise"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Floating over other apps"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Small"</item>
+ <item msgid="1666628329913333563">"Large"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN with preshared keys"</item>
diff --git a/res/values-en-rXC/arrays.xml b/res/values-en-rXC/arrays.xml
index 0144775..a5a9a76 100644
--- a/res/values-en-rXC/arrays.xml
+++ b/res/values-en-rXC/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Yellow on blue"</item>
<item msgid="747238414788976867">"Custom"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Floating over other apps"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Small"</item>
+ <item msgid="1666628329913333563">"Large"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN with pre-shared keys"</item>
diff --git a/res/values-es-rUS/arrays.xml b/res/values-es-rUS/arrays.xml
index af9cd1e..37f174e 100644
--- a/res/values-es-rUS/arrays.xml
+++ b/res/values-es-rUS/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarillo sobre azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flotando encima de otras apps"</item>
+ <item msgid="3605616699204153590">"Barra de navegación"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeño"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"Claves precompartidas de VPN L2TP/IPSec"</item>
diff --git a/res/values-es/arrays.xml b/res/values-es/arrays.xml
index 650379f..6c757bb 100644
--- a/res/values-es/arrays.xml
+++ b/res/values-es/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarillo sobre azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Se muestra sobre otras aplicaciones"</item>
+ <item msgid="3605616699204153590">"Barra de navegación"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeño"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"Red privada virtual PPTP"</item>
<item msgid="2552427673212085780">"Red privada virtual L2TP/IPSec con claves precompartidas"</item>
diff --git a/res/values-et/arrays.xml b/res/values-et/arrays.xml
index c10f340..e5d08c9 100644
--- a/res/values-et/arrays.xml
+++ b/res/values-et/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Kollane sinisel"</item>
<item msgid="747238414788976867">"Kohandatud"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Hõljub teiste rakenduste kohal"</item>
+ <item msgid="3605616699204153590">"Navigeerimisriba"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Väike"</item>
+ <item msgid="1666628329913333563">"Suur"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN eeljagatud võtmetega"</item>
diff --git a/res/values-eu/arrays.xml b/res/values-eu/arrays.xml
index 3a4694b..e558843 100644
--- a/res/values-eu/arrays.xml
+++ b/res/values-eu/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Horia urdinaren gainean"</item>
<item msgid="747238414788976867">"Pertsonalizatua"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Beste aplikazio batzuen gainean"</item>
+ <item msgid="3605616699204153590">"Nabigazio-barra"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Txikia"</item>
+ <item msgid="1666628329913333563">"Handia"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPNa aurrez partekatutako gakoekin"</item>
diff --git a/res/values-fa/arrays.xml b/res/values-fa/arrays.xml
index 186afff..fabe268 100644
--- a/res/values-fa/arrays.xml
+++ b/res/values-fa/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"زرد در آبی"</item>
<item msgid="747238414788976867">"سفارشی"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"شناور روی برنامههای دیگر"</item>
+ <item msgid="3605616699204153590">"نوار پیمایش"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"کوچک"</item>
+ <item msgid="1666628329913333563">"بزرگ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN با کلیدهای از قبل به اشتراک گذاشته شده"</item>
diff --git a/res/values-fi/arrays.xml b/res/values-fi/arrays.xml
index bdf4085..cedbcaf 100644
--- a/res/values-fi/arrays.xml
+++ b/res/values-fi/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Keltainen sinisellä"</item>
<item msgid="747238414788976867">"Muokattu"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Kelluu muiden sovellusten päällä"</item>
+ <item msgid="3605616699204153590">"Siirtymispalkki"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pieni"</item>
+ <item msgid="1666628329913333563">"Suuri"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Esijaettuun avaimeen perustuva L2TP-/IPSec-VPN-verkko"</item>
diff --git a/res/values-fr-rCA/arrays.xml b/res/values-fr-rCA/arrays.xml
index daf452c..0035d24 100644
--- a/res/values-fr-rCA/arrays.xml
+++ b/res/values-fr-rCA/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Texte jaune sur fond bleu"</item>
<item msgid="747238414788976867">"Personnalisé"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flottant par-dessus d\'autres applis"</item>
+ <item msgid="3605616699204153590">"Barre de navigation"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Petite"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"RPV PPTP"</item>
<item msgid="2552427673212085780">"RPV L2TP/IPSec avec clés pré-partagées"</item>
diff --git a/res/values-fr/arrays.xml b/res/values-fr/arrays.xml
index c5f3c4a..39954d0 100644
--- a/res/values-fr/arrays.xml
+++ b/res/values-fr/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Texte jaune sur fond bleu"</item>
<item msgid="747238414788976867">"Personnalisé"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flottant sur les autres applis"</item>
+ <item msgid="3605616699204153590">"Barre de navigation"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Petit"</item>
+ <item msgid="1666628329913333563">"Grand"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec avec clés pré-partagées"</item>
diff --git a/res/values-gl/arrays.xml b/res/values-gl/arrays.xml
index 8de9e56..e688500 100644
--- a/res/values-gl/arrays.xml
+++ b/res/values-gl/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarelo sobre azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flotando sobre outras aplicacións"</item>
+ <item msgid="3605616699204153590">"Barra de navegación"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeno"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec con claves precompartidas"</item>
diff --git a/res/values-gu/arrays.xml b/res/values-gu/arrays.xml
index e4d4264..555707c 100644
--- a/res/values-gu/arrays.xml
+++ b/res/values-gu/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"વાદળી પર પીળી"</item>
<item msgid="747238414788976867">"કસ્ટમ"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"અન્ય ઍપની ઉપર ફ્લોટિંગ"</item>
+ <item msgid="3605616699204153590">"નૅવિગેશન બાર"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"નાનું"</item>
+ <item msgid="1666628329913333563">"મોટું"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"પહેલાંથી શેર કરેલ કીઝ સાથે L2TP/IPSec VPN"</item>
diff --git a/res/values-hi/arrays.xml b/res/values-hi/arrays.xml
index c719208..d7cf7a2 100644
--- a/res/values-hi/arrays.xml
+++ b/res/values-hi/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"नीले पर पीला"</item>
<item msgid="747238414788976867">"पसंद के मुताबिक"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"दूसरे ऐप्लिकेशन के ऊपर फ़्लोट कर रहा है"</item>
+ <item msgid="3605616699204153590">"नेविगेशन बार"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"छोटा"</item>
+ <item msgid="1666628329913333563">"बड़ा"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"पहले से शेयर की गई कुंजी के साथ L2TP/IPSec VPN"</item>
diff --git a/res/values-hr/arrays.xml b/res/values-hr/arrays.xml
index 15583bf..a792e90 100644
--- a/res/values-hr/arrays.xml
+++ b/res/values-hr/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Žuto na plavom"</item>
<item msgid="747238414788976867">"Prilagođeno"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Plutanje preko drugih aplikacija"</item>
+ <item msgid="3605616699204153590">"Navigacijska traka"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Malen"</item>
+ <item msgid="1666628329913333563">"Velik"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN s unaprijed dijeljenim ključevima"</item>
diff --git a/res/values-hu/arrays.xml b/res/values-hu/arrays.xml
index 41ba9d5..a253054 100644
--- a/res/values-hu/arrays.xml
+++ b/res/values-hu/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Kék alapon sárga"</item>
<item msgid="747238414788976867">"Egyedi"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Lebegés más alkalmazások fölött"</item>
+ <item msgid="3605616699204153590">"Navigációs sáv"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kicsi"</item>
+ <item msgid="1666628329913333563">"Nagy"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN megosztott kulcsokkal"</item>
diff --git a/res/values-hy/arrays.xml b/res/values-hy/arrays.xml
index 605f265..a39b732 100644
--- a/res/values-hy/arrays.xml
+++ b/res/values-hy/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Դեղինը կապույտի վրա"</item>
<item msgid="747238414788976867">"Հատուկ"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Ցուցադրվում է այլ հավելվածների վրայից"</item>
+ <item msgid="3605616699204153590">"Նավիգացիայի գոտի"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Փոքր"</item>
+ <item msgid="1666628329913333563">"Մեծ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN նախորոշված ստեղներով"</item>
diff --git a/res/values-in/arrays.xml b/res/values-in/arrays.xml
index f2a40a7..c46d3c8 100644
--- a/res/values-in/arrays.xml
+++ b/res/values-in/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Kuning berlatar biru"</item>
<item msgid="747238414788976867">"Khusus"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Mengambang di atas aplikasi lain"</item>
+ <item msgid="3605616699204153590">"Menu navigasi"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kecil"</item>
+ <item msgid="1666628329913333563">"Besar"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN dengan kunci pra-bagi"</item>
diff --git a/res/values-is/arrays.xml b/res/values-is/arrays.xml
index b67be26..b8739ca 100644
--- a/res/values-is/arrays.xml
+++ b/res/values-is/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Gult á bláu"</item>
<item msgid="747238414788976867">"Sérsniðið"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Fljótandi yfir öðrum forritum"</item>
+ <item msgid="3605616699204153590">"Yfirlitsstika"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Lítill"</item>
+ <item msgid="1666628329913333563">"Stór"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN með lyklum sem hefur verið deilt"</item>
diff --git a/res/values-it/arrays.xml b/res/values-it/arrays.xml
index a4c0443..717a4da 100644
--- a/res/values-it/arrays.xml
+++ b/res/values-it/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Giallo su blu"</item>
<item msgid="747238414788976867">"Personalizzato"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Mobile sopra altre app"</item>
+ <item msgid="3605616699204153590">"Barra di navigazione"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Piccolo"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec con chiavi precondivise"</item>
diff --git a/res/values-iw/arrays.xml b/res/values-iw/arrays.xml
index cfa1350..7ba72f5 100644
--- a/res/values-iw/arrays.xml
+++ b/res/values-iw/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"צהוב על גבי כחול"</item>
<item msgid="747238414788976867">"מותאם אישית"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"צף מעל אפליקציות אחרות"</item>
+ <item msgid="3605616699204153590">"סרגל ניווט"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"קטן"</item>
+ <item msgid="1666628329913333563">"גדול"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN עם מפתחות משותפים מראש"</item>
diff --git a/res/values-ja/arrays.xml b/res/values-ja/arrays.xml
index 6a4e186..4431a17 100644
--- a/res/values-ja/arrays.xml
+++ b/res/values-ja/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"青地に黄色"</item>
<item msgid="747238414788976867">"カスタム"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"他のアプリの上にフローティング"</item>
+ <item msgid="3605616699204153590">"ナビゲーション バー"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"小"</item>
+ <item msgid="1666628329913333563">"大"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"事前共有鍵付きのL2TP/IPSec VPN"</item>
diff --git a/res/values-ka/arrays.xml b/res/values-ka/arrays.xml
index d6c826e..8e0e04f 100644
--- a/res/values-ka/arrays.xml
+++ b/res/values-ka/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ყვითელი ლურჯზე"</item>
<item msgid="747238414788976867">"მორგებული"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"გადაფარვითი ჩვენება სხვა აპებზე ლივლივით"</item>
+ <item msgid="3605616699204153590">"ნავიგაციის ზოლი"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"პატარა"</item>
+ <item msgid="1666628329913333563">"დიდი"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN წინასწარ გაზიარებული ღილაკებით"</item>
diff --git a/res/values-kk/arrays.xml b/res/values-kk/arrays.xml
index b499a8e..58922a3 100644
--- a/res/values-kk/arrays.xml
+++ b/res/values-kk/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Көк түсте сарымен"</item>
<item msgid="747238414788976867">"Басқа"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Басқа қолданбалар үстінен қалқу"</item>
+ <item msgid="3605616699204153590">"Навигация жолағы"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Кішкентай"</item>
+ <item msgid="1666628329913333563">"Үлкен"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec, пернелері ортақ ВЖЖ"</item>
diff --git a/res/values-km/arrays.xml b/res/values-km/arrays.xml
index 052f638..9e7f634 100644
--- a/res/values-km/arrays.xml
+++ b/res/values-km/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ពណ៌លឿងលើពណ៌ខៀវ"</item>
<item msgid="747238414788976867">"តាមបំណង"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"កំពុងអណ្ដែតពីលើកម្មវិធីផ្សេងទៀត"</item>
+ <item msgid="3605616699204153590">"របាររុករក"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"តូច"</item>
+ <item msgid="1666628329913333563">"ធំ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN ជាមួយសោចែករំលែកជាមុន"</item>
diff --git a/res/values-kn/arrays.xml b/res/values-kn/arrays.xml
index 4ee2b13..eeca4c9 100644
--- a/res/values-kn/arrays.xml
+++ b/res/values-kn/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ನೀಲಿ ಬಣ್ಣದಲ್ಲಿ ಹಳದಿ"</item>
<item msgid="747238414788976867">"ಕಸ್ಟಮ್"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ಇತರೆ ಆ್ಯಪ್ಗಳಲ್ಲೂ ರನ್ ಆಗುತ್ತಿದೆ"</item>
+ <item msgid="3605616699204153590">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ಸಣ್ಣದು"</item>
+ <item msgid="1666628329913333563">"ದೊಡ್ಡದು"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"ಪೂರ್ವ-ಹಂಚಿಕೆಯಾದ ಕೀಗಳನ್ನು ಹೊಂದಿರುವ L2TP/IPSec VPN"</item>
diff --git a/res/values-ko/arrays.xml b/res/values-ko/arrays.xml
index e574fa5..a806d87 100644
--- a/res/values-ko/arrays.xml
+++ b/res/values-ko/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"파란색 바탕에 노란색"</item>
<item msgid="747238414788976867">"맞춤설정"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"다른 앱 위에 플로팅"</item>
+ <item msgid="3605616699204153590">"탐색 메뉴"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"작게"</item>
+ <item msgid="1666628329913333563">"크게"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"사전 공유 키를 사용하는 L2TP/IPSec VPN"</item>
diff --git a/res/values-ky/arrays.xml b/res/values-ky/arrays.xml
index 9e70efd..dab63c2 100644
--- a/res/values-ky/arrays.xml
+++ b/res/values-ky/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Сары көктө"</item>
<item msgid="747238414788976867">"Өзгөчөлөнгөн"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Башка колдонмолордун үстүнөн көрсөтүү"</item>
+ <item msgid="3605616699204153590">"Чабыттоо тилкеси"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Кичине"</item>
+ <item msgid="1666628329913333563">"Чоң"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Алдын ала бөлүшүлгөн ачкычтары бар L2TP/IPSec VPN"</item>
diff --git a/res/values-lo/arrays.xml b/res/values-lo/arrays.xml
index cc5f45b..47906e0 100644
--- a/res/values-lo/arrays.xml
+++ b/res/values-lo/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ສີເຫຼືອງພື້ນຟ້າ"</item>
<item msgid="747238414788976867">"ກຳນົດເອງ"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ລອຍຢູ່ເທິງແອັບອື່ນ"</item>
+ <item msgid="3605616699204153590">"ແຖບການນຳທາງ"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ນ້ອຍ"</item>
+ <item msgid="1666628329913333563">"ໃຫຍ່"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN ທີ່ມີກະແຈທີ່ແບ່ງປັນລ່ວງໜ້າ"</item>
diff --git a/res/values-lt/arrays.xml b/res/values-lt/arrays.xml
index c04c608..a2351d1 100644
--- a/res/values-lt/arrays.xml
+++ b/res/values-lt/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Geltonas ant mėlyno"</item>
<item msgid="747238414788976867">"Tinkintas"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Rodomas virš kitų programų"</item>
+ <item msgid="3605616699204153590">"Naršymo juosta"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Mažas"</item>
+ <item msgid="1666628329913333563">"Didelis"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP / „IPSec“ VPN su iš anksto bendrinamais raktais"</item>
diff --git a/res/values-lv/arrays.xml b/res/values-lv/arrays.xml
index 54e659a..5bd48d0 100644
--- a/res/values-lv/arrays.xml
+++ b/res/values-lv/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Dzeltens uz zila"</item>
<item msgid="747238414788976867">"Pielāgots"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Peldoša poga virs lietotnēm"</item>
+ <item msgid="3605616699204153590">"Navigācijas joslā"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Maza"</item>
+ <item msgid="1666628329913333563">"Liela"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Otrā slāņa tunelēšanas protokola/protokola IPsec VPN ar iepriekš kopīgotām atslēgām"</item>
diff --git a/res/values-mk/arrays.xml b/res/values-mk/arrays.xml
index 16e12e4..d3f382c 100644
--- a/res/values-mk/arrays.xml
+++ b/res/values-mk/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Жолти на сино"</item>
<item msgid="747238414788976867">"Приспособено"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Лебди над други апликации"</item>
+ <item msgid="3605616699204153590">"Лента за навигација"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Мало"</item>
+ <item msgid="1666628329913333563">"Големо"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN со претходно споделени клучеви"</item>
diff --git a/res/values-ml/arrays.xml b/res/values-ml/arrays.xml
index 0553306..cd8edbc 100644
--- a/res/values-ml/arrays.xml
+++ b/res/values-ml/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"നീലയിൽ മഞ്ഞ"</item>
<item msgid="747238414788976867">"ഇഷ്ടാനുസൃതം"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"മറ്റ് ആപ്പുകൾക്ക് മുകളിലൂടെ നീങ്ങുന്നു"</item>
+ <item msgid="3605616699204153590">"നാവിഗേഷൻ ബാർ"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ചെറുത്"</item>
+ <item msgid="1666628329913333563">"വലുത്"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"മുമ്പ് പങ്കിട്ട കീകൾ ഉപയോഗിക്കുന്ന L2TP/IPSec VPN"</item>
diff --git a/res/values-mn/arrays.xml b/res/values-mn/arrays.xml
index 0430de7..ddc8d40 100644
--- a/res/values-mn/arrays.xml
+++ b/res/values-mn/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Цэнхэр дээр шар"</item>
<item msgid="747238414788976867">"Өөрчлөх"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Бусад апп дээр хөвөх"</item>
+ <item msgid="3605616699204153590">"Навигацын самбар"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Жижиг"</item>
+ <item msgid="1666628329913333563">"Том"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Урьдчилан хуваалцсан L2TP/IPSec VPN түлхүүртэй"</item>
diff --git a/res/values-mr/arrays.xml b/res/values-mr/arrays.xml
index 7f7abb4..60dc05a 100644
--- a/res/values-mr/arrays.xml
+++ b/res/values-mr/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"निळ्यावर पिवळे"</item>
<item msgid="747238414788976867">"कस्टम"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"इतर ॲप्सवर फ्लोट करतात"</item>
+ <item msgid="3605616699204153590">"नेव्हिगेशन बार"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"लहान"</item>
+ <item msgid="1666628329913333563">"मोठा"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"पूर्व-शेअर की सह L2TP/IPSec VPN"</item>
diff --git a/res/values-ms/arrays.xml b/res/values-ms/arrays.xml
index 0dbd497..793f729 100644
--- a/res/values-ms/arrays.xml
+++ b/res/values-ms/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Kuning pada biru"</item>
<item msgid="747238414788976867">"Peribadi"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Terapung di atas apl lain"</item>
+ <item msgid="3605616699204153590">"Bar navigasi"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kecil"</item>
+ <item msgid="1666628329913333563">"Besar"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec dengan kunci prakongsi"</item>
diff --git a/res/values-my/arrays.xml b/res/values-my/arrays.xml
index b425af6..ad7140e 100644
--- a/res/values-my/arrays.xml
+++ b/res/values-my/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"အပြာပေါ်အဝါ"</item>
<item msgid="747238414788976867">"စိတ်ကြိုက်"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"အခြားအက်ပ်များပေါ်တွင် မြင်ရခြင်း"</item>
+ <item msgid="3605616699204153590">"လမ်းညွှန်ဘား"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"သေး"</item>
+ <item msgid="1666628329913333563">"ကြီး"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"ကြိုတင် ပေးထားတဲ့ ကီးပါရှိသောL2TP/IPSec VPN"</item>
diff --git a/res/values-nb/arrays.xml b/res/values-nb/arrays.xml
index 02044dc..d893b40 100644
--- a/res/values-nb/arrays.xml
+++ b/res/values-nb/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Gult på blått"</item>
<item msgid="747238414788976867">"Egendefinert"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flytende over andre apper"</item>
+ <item msgid="3605616699204153590">"Navigasjonsrad"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Liten"</item>
+ <item msgid="1666628329913333563">"Stor"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN med forhåndsdelte nøkler"</item>
diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml
index bacf02a..40e4b30 100644
--- a/res/values-ne/arrays.xml
+++ b/res/values-ne/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"नीलोमा पहेँलो"</item>
<item msgid="747238414788976867">"आफू अनुकूल"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"अन्य एपहरूमाथि तैरने"</item>
+ <item msgid="3605616699204153590">"नेभिगेसन बार"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"सानो"</item>
+ <item msgid="1666628329913333563">"ठुलो"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN पूर्व साझेदारी कुञ्जीकासँग"</item>
diff --git a/res/values-nl/arrays.xml b/res/values-nl/arrays.xml
index 627fa59..eafae00 100644
--- a/res/values-nl/arrays.xml
+++ b/res/values-nl/arrays.xml
@@ -274,7 +274,7 @@
<item msgid="2297727967385895059">"locatie met hoog energieverbruik controleren"</item>
<item msgid="8700593962030471569">"gebruiksstatistieken ophalen"</item>
<item msgid="4140820386622184831">"microfoon uit-/aanzetten"</item>
- <item msgid="317746827951691657">"toast weergeven"</item>
+ <item msgid="317746827951691657">"toast tonen"</item>
<item msgid="5679422988212309779">"media projecteren"</item>
<item msgid="6454031639780101439">"VPN activeren"</item>
<item msgid="2441327072846850561">"achtergrond schrijven"</item>
@@ -341,7 +341,7 @@
<item msgid="5186169827582545242">"Locatie"</item>
<item msgid="6122293931012635638">"Gebruiksstatistieken ophalen"</item>
<item msgid="2526677383312751932">"Microfoon uit-/aanzetten"</item>
- <item msgid="4000577305179914546">"Toast weergeven"</item>
+ <item msgid="4000577305179914546">"Toast tonen"</item>
<item msgid="8660207174515570558">"Media projecteren"</item>
<item msgid="3904996949561946108">"VPN activeren"</item>
<item msgid="504052124101832515">"Achtergrond schrijven"</item>
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Geel op blauw"</item>
<item msgid="747238414788976867">"Aangepast"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Zwevend over andere apps"</item>
+ <item msgid="3605616699204153590">"Navigatiebalk"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Klein"</item>
+ <item msgid="1666628329913333563">"Groot"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP-VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec-VPN met van tevoren gedeelde sleutels"</item>
diff --git a/res/values-or/arrays.xml b/res/values-or/arrays.xml
index 10a8ca8..dfdaa2e 100644
--- a/res/values-or/arrays.xml
+++ b/res/values-or/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ନୀଳ ଉପରେ ହଳଦିଆ"</item>
<item msgid="747238414788976867">"କଷ୍ଟମ୍"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ଅନ୍ୟ ଆପଗୁଡ଼ିକ ଉପରେ ଫ୍ଲୋଟ୍ ହେଉଛି"</item>
+ <item msgid="3605616699204153590">"ନାଭିଗେସନ୍ ବାର୍"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ଛୋଟ"</item>
+ <item msgid="1666628329913333563">"ବଡ଼"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"ପୂର୍ବରୁ ସେୟାର୍ ହୋଇଥିବା କୀଗୁଡ଼ିକ ସହ L2TP/IPSec VPN"</item>
diff --git a/res/values-pa/arrays.xml b/res/values-pa/arrays.xml
index b19666c..4838f68 100644
--- a/res/values-pa/arrays.xml
+++ b/res/values-pa/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"ਨੀਲੇ \'ਤੇ ਪੀਲਾ"</item>
<item msgid="747238414788976867">"ਵਿਉਂਂਤੀ"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ਦੂਜੀਆਂ ਐਪਾਂ \'ਤੇ ਫ਼ਲੋਟ ਕਰਨਾ"</item>
+ <item msgid="3605616699204153590">"ਦਿਸ਼ਾ-ਨਿਰਦੇਸ਼ ਪੱਟੀ"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"ਛੋਟਾ"</item>
+ <item msgid="1666628329913333563">"ਵੱਡਾ"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"ਪ੍ਰੀ-ਸ਼ੇਅਰ ਕੀਤੀਆਂ ਕੁੰਜੀਆਂ ਨਾਲ L2TP/IPSec VPN"</item>
diff --git a/res/values-pl/arrays.xml b/res/values-pl/arrays.xml
index 4a47bac..518e302 100644
--- a/res/values-pl/arrays.xml
+++ b/res/values-pl/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Żółty na niebieskim"</item>
<item msgid="747238414788976867">"Niestandardowy"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Pływający nad innymi aplikacjami"</item>
+ <item msgid="3605616699204153590">"Pasek nawigacyjny"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Mały"</item>
+ <item msgid="1666628329913333563">"Duży"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"Sieć VPN wykorzystująca protokół PPTP"</item>
<item msgid="2552427673212085780">"Sieć VPN wykorzystująca protokół L2TP/IPSec z kluczami wspólnymi"</item>
diff --git a/res/values-pt-rBR/arrays.xml b/res/values-pt-rBR/arrays.xml
index 58b4e61..640ebbd 100644
--- a/res/values-pt-rBR/arrays.xml
+++ b/res/values-pt-rBR/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarelo em azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flutuando sobre outros apps"</item>
+ <item msgid="3605616699204153590">"Barra de navegação"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeno"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN com chaves pré-compartilhadas"</item>
diff --git a/res/values-pt-rPT/arrays.xml b/res/values-pt-rPT/arrays.xml
index d387923..1937541 100644
--- a/res/values-pt-rPT/arrays.xml
+++ b/res/values-pt-rPT/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarelo em azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flutuante sobre outras apps"</item>
+ <item msgid="3605616699204153590">"Barra de navegação"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeno"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"VPN L2TP/IPSec com chaves pré-partilhadas"</item>
diff --git a/res/values-pt/arrays.xml b/res/values-pt/arrays.xml
index 58b4e61..640ebbd 100644
--- a/res/values-pt/arrays.xml
+++ b/res/values-pt/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Amarelo em azul"</item>
<item msgid="747238414788976867">"Personalizado"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flutuando sobre outros apps"</item>
+ <item msgid="3605616699204153590">"Barra de navegação"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Pequeno"</item>
+ <item msgid="1666628329913333563">"Grande"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN com chaves pré-compartilhadas"</item>
diff --git a/res/values-ro/arrays.xml b/res/values-ro/arrays.xml
index c4c6284..4f282e2 100644
--- a/res/values-ro/arrays.xml
+++ b/res/values-ro/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Galben pe albastru"</item>
<item msgid="747238414788976867">"Personalizat"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Se afișează peste alte aplicații"</item>
+ <item msgid="3605616699204153590">"Bară de navigare"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Mic"</item>
+ <item msgid="1666628329913333563">"Mare"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN cu chei predistribuite"</item>
diff --git a/res/values-ru/arrays.xml b/res/values-ru/arrays.xml
index 98d2439..cdcf10b 100644
--- a/res/values-ru/arrays.xml
+++ b/res/values-ru/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Желтый на синем"</item>
<item msgid="747238414788976867">"Специальный"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Поверх других приложений"</item>
+ <item msgid="3605616699204153590">"Панель навигации"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Маленькая кнопка"</item>
+ <item msgid="1666628329913333563">"Большая кнопка"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN-подключение по протоколу PPTP"</item>
<item msgid="2552427673212085780">"VPN-соединение по протоколу L2TP/IPSec с общими ключами"</item>
diff --git a/res/values-si/arrays.xml b/res/values-si/arrays.xml
index f3944d9..c0f8dbb 100644
--- a/res/values-si/arrays.xml
+++ b/res/values-si/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"නිල් මත කහ"</item>
<item msgid="747238414788976867">"අභිරුචි"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"යෙදුම්වලට උඩින් පාවීම"</item>
+ <item msgid="3605616699204153590">"සංචලන තීරුව"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"කුඩා"</item>
+ <item msgid="1666628329913333563">"විශාල"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"පෙර බෙදාගත් යතුරු සමඟ L2TP/IPSec VPN"</item>
diff --git a/res/values-sk/arrays.xml b/res/values-sk/arrays.xml
index 5ec1c81..2d6e38a 100644
--- a/res/values-sk/arrays.xml
+++ b/res/values-sk/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Žlté na modrom"</item>
<item msgid="747238414788976867">"Vlastné"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Plávajúce nad ostatnými aplikáciami"</item>
+ <item msgid="3605616699204153590">"Navigačný panel"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Malé"</item>
+ <item msgid="1666628329913333563">"Veľké"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN s protokolom PPTP"</item>
<item msgid="2552427673212085780">"Sieť VPN založená na protokole L2TP/IPSec s predzdieľanými kľúčmi"</item>
diff --git a/res/values-sl/arrays.xml b/res/values-sl/arrays.xml
index 8ed2d74..9931395 100644
--- a/res/values-sl/arrays.xml
+++ b/res/values-sl/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Rumeno na modrem"</item>
<item msgid="747238414788976867">"Po meri"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Prekrivanje drugih aplikacij"</item>
+ <item msgid="3605616699204153590">"Vrstica za krmarjenje"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Majhen"</item>
+ <item msgid="1666628329913333563">"Velik"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN s ključi v predhodni skupni rabi"</item>
diff --git a/res/values-sq/arrays.xml b/res/values-sq/arrays.xml
index 9a348bf..4ade61a 100644
--- a/res/values-sq/arrays.xml
+++ b/res/values-sq/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"E verdhë mbi të kaltër"</item>
<item msgid="747238414788976867">"Të personalizuara"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Pluskon mbi aplikacionet e tjera"</item>
+ <item msgid="3605616699204153590">"Shiriti i navigimit"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"I vogël"</item>
+ <item msgid="1666628329913333563">"I madh"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"VPN PPTP"</item>
<item msgid="2552427673212085780">"Rrjeti VPN L2TP/IPSec me çelësat e ndarë paraprakisht"</item>
diff --git a/res/values-sr/arrays.xml b/res/values-sr/arrays.xml
index 18bf8fe..ca536c9 100644
--- a/res/values-sr/arrays.xml
+++ b/res/values-sr/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Жуто на плаво"</item>
<item msgid="747238414788976867">"Прилагођено"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Плута преко других апликација"</item>
+ <item msgid="3605616699204153590">"Трака за навигацију"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Мала"</item>
+ <item msgid="1666628329913333563">"Велика"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN са унапред дељеним кључевима"</item>
diff --git a/res/values-sv/arrays.xml b/res/values-sv/arrays.xml
index 5d3dc63..365e4fa 100644
--- a/res/values-sv/arrays.xml
+++ b/res/values-sv/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Gult på blått"</item>
<item msgid="747238414788976867">"Anpassat"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Flytande över andra appar"</item>
+ <item msgid="3605616699204153590">"Navigeringsfält"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Liten"</item>
+ <item msgid="1666628329913333563">"Stor"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP-VPN"</item>
<item msgid="2552427673212085780">"L2TP-/IPSec-VPN med nycklar som delats i förväg"</item>
diff --git a/res/values-sw/arrays.xml b/res/values-sw/arrays.xml
index 89fb40c..0dd92d5 100644
--- a/res/values-sw/arrays.xml
+++ b/res/values-sw/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Manjano kwenye samawati"</item>
<item msgid="747238414788976867">"Maalum"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Kuelea juu ya programu zingine"</item>
+ <item msgid="3605616699204153590">"Sehemu ya viungo muhimu"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kidogo"</item>
+ <item msgid="1666628329913333563">"Kikubwa"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN na vitufe vilivyoshirikishwa kabla"</item>
diff --git a/res/values-ta/arrays.xml b/res/values-ta/arrays.xml
index e5c4a79..96f97c0 100644
--- a/res/values-ta/arrays.xml
+++ b/res/values-ta/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"நீலத்தில் மஞ்சள்"</item>
<item msgid="747238414788976867">"பிரத்தியேகம்"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"பிற ஆப்ஸ் மீது தோன்றுதல்"</item>
+ <item msgid="3605616699204153590">"வழிசெலுத்தல் பட்டி"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"சிறியது"</item>
+ <item msgid="1666628329913333563">"பெரியது"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"பாதுகாப்பு விசைகளுடன் கூடிய L2TP/IPSec VPN"</item>
diff --git a/res/values-te/arrays.xml b/res/values-te/arrays.xml
index 18bb6bc..00a56f7 100644
--- a/res/values-te/arrays.xml
+++ b/res/values-te/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"నీలి నేపథ్యంలో పసుపు రంగు"</item>
<item msgid="747238414788976867">"అనుకూలం"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ఇతర యాప్ల మీద తేలియాడుతోంది"</item>
+ <item msgid="3605616699204153590">"నావిగేషన్ బార్"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"చిన్నది"</item>
+ <item msgid="1666628329913333563">"పెద్దది"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"పూర్వ-భాగస్వామ్య కీలతో L2TP/IPSec VPN"</item>
diff --git a/res/values-th/arrays.xml b/res/values-th/arrays.xml
index 1e4de9c3..a659bda 100644
--- a/res/values-th/arrays.xml
+++ b/res/values-th/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"สีเหลืองบนพื้นสีน้ำเงิน"</item>
<item msgid="747238414788976867">"กำหนดเอง"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"ลอยเหนือแอปอื่นๆ"</item>
+ <item msgid="3605616699204153590">"แถบนำทาง"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"เล็ก"</item>
+ <item msgid="1666628329913333563">"ใหญ่"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN พร้อมด้วยคีย์ที่แชร์ไว้ล่วงหน้า"</item>
diff --git a/res/values-tl/arrays.xml b/res/values-tl/arrays.xml
index 938eff5..b65b170 100644
--- a/res/values-tl/arrays.xml
+++ b/res/values-tl/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Dilaw sa asul"</item>
<item msgid="747238414788976867">"Custom"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Nasa ibabaw ng iba pang app"</item>
+ <item msgid="3605616699204153590">"Navigation bar"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Maliit"</item>
+ <item msgid="1666628329913333563">"Malaki"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN na may mga paunang nabahaging key"</item>
diff --git a/res/values-tr/arrays.xml b/res/values-tr/arrays.xml
index 5ed1d6c..c55df18 100644
--- a/res/values-tr/arrays.xml
+++ b/res/values-tr/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Mavi üzerine sarı"</item>
<item msgid="747238414788976867">"Özel"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Diğer uygulamaların üzerinde kayan"</item>
+ <item msgid="3605616699204153590">"Gezinme çubuğu"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Küçük"</item>
+ <item msgid="1666628329913333563">"Büyük"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"Önceden paylaşılan anahtara sahip L2TP/IPSec VPN"</item>
diff --git a/res/values-uk/arrays.xml b/res/values-uk/arrays.xml
index 5bbc7ac..9c85a16 100644
--- a/res/values-uk/arrays.xml
+++ b/res/values-uk/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Жовтий на синьому"</item>
<item msgid="747238414788976867">"Спеціальний"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Плаває поверх інших додатків"</item>
+ <item msgid="3605616699204153590">"Панель навігації"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Мала"</item>
+ <item msgid="1666628329913333563">"Велика"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN зі спільними ключами"</item>
diff --git a/res/values-ur/arrays.xml b/res/values-ur/arrays.xml
index a336c3c..4e5af0b 100644
--- a/res/values-ur/arrays.xml
+++ b/res/values-ur/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"نیلے پر پیلا"</item>
<item msgid="747238414788976867">"حسب ضرورت"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"دیگر ایپس پر فلوٹنگ ہو رہی ہے"</item>
+ <item msgid="3605616699204153590">"نیویگیشن بار"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"چھوٹا"</item>
+ <item msgid="1666628329913333563">"بڑا"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"پہلے سے اشتراک کردہ کلیدوں کے ساتھ L2TP/IPSec VPN"</item>
diff --git a/res/values-uz/arrays.xml b/res/values-uz/arrays.xml
index 56f7063..c24ba87 100644
--- a/res/values-uz/arrays.xml
+++ b/res/values-uz/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Ko‘k ustida sariq"</item>
<item msgid="747238414788976867">"Maxsus"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Boshqa ilovalar ustidan chiqishi"</item>
+ <item msgid="3605616699204153590">"Navigatsiya paneli"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Kichik"</item>
+ <item msgid="1666628329913333563">"Yirik"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN avval ulashilgan kalitlar bilan"</item>
diff --git a/res/values-vi/arrays.xml b/res/values-vi/arrays.xml
index ad319ac..a1158fc 100644
--- a/res/values-vi/arrays.xml
+++ b/res/values-vi/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Chữ vàng trên nền xanh lam"</item>
<item msgid="747238414788976867">"Tùy chỉnh"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Nổi trên các ứng dụng khác"</item>
+ <item msgid="3605616699204153590">"Thanh điều hướng"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Nhỏ"</item>
+ <item msgid="1666628329913333563">"Lớn"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN có khóa chia sẻ trước"</item>
diff --git a/res/values-zh-rCN/arrays.xml b/res/values-zh-rCN/arrays.xml
index 19deb1e..073df09 100644
--- a/res/values-zh-rCN/arrays.xml
+++ b/res/values-zh-rCN/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"蓝底黄字"</item>
<item msgid="747238414788976867">"自定义"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"悬浮在其他应用的上层"</item>
+ <item msgid="3605616699204153590">"导航栏"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"小"</item>
+ <item msgid="1666628329913333563">"大"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"具有预共享密钥的 L2TP/IPSec VPN"</item>
diff --git a/res/values-zh-rHK/arrays.xml b/res/values-zh-rHK/arrays.xml
index e876134..a91a444 100644
--- a/res/values-zh-rHK/arrays.xml
+++ b/res/values-zh-rHK/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"藍底黃字"</item>
<item msgid="747238414788976867">"自訂"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"浮動顯示在其他應用程式上"</item>
+ <item msgid="3605616699204153590">"導覽列"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"小"</item>
+ <item msgid="1666628329913333563">"大"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN (預先共用密鑰)"</item>
diff --git a/res/values-zh-rTW/arrays.xml b/res/values-zh-rTW/arrays.xml
index dee72bc..1a49712 100644
--- a/res/values-zh-rTW/arrays.xml
+++ b/res/values-zh-rTW/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"藍底黃字"</item>
<item msgid="747238414788976867">"自訂"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"浮動顯示在其他應用程式上"</item>
+ <item msgid="3605616699204153590">"導覽列"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"小"</item>
+ <item msgid="1666628329913333563">"大"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"L2TP/IPSec VPN (預先共用金鑰)"</item>
diff --git a/res/values-zu/arrays.xml b/res/values-zu/arrays.xml
index 40edd11..26d2e8b 100644
--- a/res/values-zu/arrays.xml
+++ b/res/values-zu/arrays.xml
@@ -419,6 +419,14 @@
<item msgid="2836895041823327816">"Okuliphuzi kokuluhlaza"</item>
<item msgid="747238414788976867">"Ngokwezifiso"</item>
</string-array>
+ <string-array name="accessibility_button_location_selector_titles">
+ <item msgid="6485511780196327736">"Intanta phezu kwamanye ama-app"</item>
+ <item msgid="3605616699204153590">"Ibha yokuzula"</item>
+ </string-array>
+ <string-array name="accessibility_button_size_selector_titles">
+ <item msgid="7482952318152486459">"Esincane"</item>
+ <item msgid="1666628329913333563">"Esikhulu"</item>
+ </string-array>
<string-array name="vpn_types_long">
<item msgid="6621806338070912611">"PPTP VPN"</item>
<item msgid="2552427673212085780">"i-L2TP/IPSec VPN nokhiye okwabeliswaniwa ngabo"</item>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 183dd1e..2057c50 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -966,6 +966,35 @@
<item>-1</item>
</integer-array>
+ <!-- Titles for the accessibility button location. [CHAR LIMIT=35] -->
+ <string-array name="accessibility_button_location_selector_titles">
+ <item>Floating over other apps</item>
+ <item>Navigation bar</item>
+ </string-array>
+
+ <!-- Values for the accessibility button location. -->
+ <!-- Should Keep in sync with Settings.Secure.ACCESSIBILITY_BUTTON_MODE_* -->
+ <string-array name="accessibility_button_location_selector_values" translatable="false">
+ <!-- Floating over other apps -->
+ <item>1</item>
+ <!-- Navigation bar -->
+ <item>0</item>
+ </string-array>
+
+ <!-- Titles for the accessibility button size. [CHAR LIMIT=35] -->
+ <string-array name="accessibility_button_size_selector_titles">
+ <item>Small</item>
+ <item>Large</item>
+ </string-array>
+
+ <!-- Values for the accessibility button size. -->
+ <string-array name="accessibility_button_size_selector_values" translatable="false" >
+ <!-- Small -->
+ <item>0</item>
+ <!-- Large -->
+ <item>1</item>
+ </string-array>
+
<!-- Match this with the constants in VpnProfile. --> <skip />
<!-- Short names for each VPN type, not really translatable. [CHAR LIMIT=20] -->
<string-array name="vpn_types" translatable="false">
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4fef726..0ee39cd 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -57,6 +57,8 @@
<dimen name="color_mode_preview_height">320dp</dimen>
+ <dimen name="accessibility_button_preview_height">200dp</dimen>
+
<dimen name="ring_progress_bar_thickness">4dp</dimen>
<!-- Weight of the left pane in a multi-pane preference layout. -->
@@ -293,6 +295,7 @@
<!-- Accessibility Settings -->
<dimen name="accessibility_layout_margin_start_end">16dp</dimen>
<dimen name="accessibility_button_preference_padding_top_bottom">18dp</dimen>
+ <dimen name="accessibility_imageview_size">176dp</dimen>
<!-- Restricted icon in switch bar -->
<dimen name="restricted_icon_margin_end">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6dfae4b..7755da4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2416,6 +2416,10 @@
<string name="wifi_hotspot_auto_off_title">Turn off hotspot automatically</string>
<!-- Summary for the toggle to turn off hotspot automatically [CHAR LIMIT=NONE]-->
<string name="wifi_hotspot_auto_off_summary">When no devices are connected</string>
+ <!-- Title for the toggle to enable/disable the maximize compatibility [CHAR LIMIT=NONE]-->
+ <string name="wifi_hotspot_maximize_compatibility">Maximize compatibility</string>
+ <!-- Summary for the toggle to show the maximize compatibility warning message [CHAR LIMIT=NONE]-->
+ <string name="wifi_hotspot_maximize_compatibility_summary">This may reduce speed for devices connected to this hotspot and use more power</string>
<!-- Summary text when turning hotspot on -->
<string name="wifi_tether_starting">Turning hotspot on\u2026</string>
@@ -3364,6 +3368,10 @@
<string name="storage_menu_manage">Manage storage</string>
<!-- Storage setting. Keywords for Free up space. [CHAR LIMIT=NONE] -->
<string name="keywords_storage_menu_free">clean, storage</string>
+ <!-- Storage setting. Title for storage free up option. [CHAR LIMIT=30] -->
+ <string name="storage_free_up_space_title">Free up space</string>
+ <!-- Storage setting. Summary for storage free up option. [CHAR LIMIT=NONE] -->
+ <string name="storage_free_up_space_summary">Go to Files app to manage and free up space</string>
<!-- Storage setting. Title for USB transfer settings [CHAR LIMIT=30]-->
<string name="storage_title_usb">USB computer connection</string>
@@ -4041,7 +4049,7 @@
apps have access to location</item>
</plurals>
<!-- [CHAR LIMIT=50] Location settings screen, sub category for recent location access -->
- <string name="location_category_recent_location_access">Past 24 hour access</string>
+ <string name="location_category_recent_location_access">Recent access</string>
<!-- Location settings screen, displayed when there're more than three recent location access apps [CHAR LIMIT=30] -->
<string name="location_recent_location_access_see_all">See all</string>
<!-- [CHAR LIMIT=30] Location settings screen, button to bring the user to view the details of recent location access -->
@@ -5035,7 +5043,7 @@
<!-- Title for the accessibility preference screen to enable screen magnification. [CHAR LIMIT=35] -->
<string name="accessibility_screen_magnification_title">Magnification</string>
<!-- Title for the accessibility preference screen to edit magnification area. [CHAR LIMIT=35] -->
- <string name="accessibility_magnification_mode_title">Choose how to magnify</string>
+ <string name="accessibility_magnification_mode_title">Magnification type</string>
<!-- Message for the accessibility preference screen to edit magnification area dialog. [CHAR LIMIT=none] -->
<string name="accessibility_magnification_area_settings_message">Magnify your full screen, a specific area, or switch between both options</string>
<!-- Summary for the accessibility preference screen to edit full screen. [CHAR LIMIT=none] -->
@@ -5044,10 +5052,16 @@
<string name="accessibility_magnification_area_settings_window_screen_summary">Partial screen</string>
<!-- Summary for the accessibility preference screen to edit full and partial screen. [CHAR LIMIT=none] -->
<string name="accessibility_magnification_area_settings_all_summary">Switch between full and partial screen</string>
- <!-- Message for the accessibility preference screen to edit the full screen. [CHAR LIMIT=none] -->
- <string name="accessibility_magnification_area_settings_full_screen">Magnify full screen</string>
- <!-- Message for the accessibility preference screen to edit part of screen. [CHAR LIMIT=none] -->
- <string name="accessibility_magnification_area_settings_window_screen">Magnify part of screen</string>
+ <!-- dialog title for magnification mode selection. [CHAR LIMIT=35] -->
+ <string name="accessibility_magnification_mode_dialog_title">Choose how to magnify</string>
+ <!-- Option title of full-screen magnification mode in the mode selection dialog. [CHAR LIMIT=50] -->
+ <string name="accessibility_magnification_mode_dialog_option_full_screen">Magnify full screen</string>
+ <!-- Option title of window magnification mode in the mode selection dialog. [CHAR LIMIT=50] -->
+ <string name="accessibility_magnification_mode_dialog_option_window">Magnify part of screen</string>
+ <!-- Option title of full magnification mode in the mode selection dialog. [CHAR LIMIT=50] -->
+ <string name="accessibility_magnification_mode_dialog_option_switch">Switch between full and partial screen</string>
+ <!-- Message of the magnification mode option to choose the magnification mode. [CHAR LIMIT=none] -->
+ <string name="accessibility_magnification_area_settings_mode_switch_summary">Tap the switch button to move between both options</string>
<!-- Title for the accessibility magnification switch shortcut dialog. [CHAR LIMIT=48] -->
<string name="accessibility_magnification_switch_shortcut_title">Switch to accessibility button?</string>
<!-- Message for the accessibility magnification switch shortcut dialog. [CHAR LIMIT=none] -->
@@ -5106,6 +5120,8 @@
<string name="accessibility_tutorial_dialog_title_gesture_settings">Use new accessibility gesture</string>
<!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the 3-button nav bar. [CHAR LIMIT=NONE] -->
<string name="accessibility_tutorial_dialog_message_button">To use this feature, tap the accessibility button <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> on the bottom of your screen.\n\nTo switch between features, touch & hold the accessibility button.</string>
+ <!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using the accessibility floating button. [CHAR LIMIT=100] -->
+ <string name="accessibility_tutorial_dialog_message_floating_button">To use this feature, tap the accessibility button on your screen.</string>
<!-- Instruction for the accessibility tutorial dialog in accessibility service with volume keys. [CHAR LIMIT=100] -->
<string name="accessibility_tutorial_dialog_message_volume">To use this feature, press & hold both volume keys.</string>
<!-- Instruction for the accessibility tutorial dialog in accessibility service with triple tap. [CHAR LIMIT=100] -->
@@ -5136,6 +5152,8 @@
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture">Swipe up from the bottom of the screen with 2 fingers.\n\nTo switch between features, swipe up with 2 fingers and hold.</string>
<!-- Summary for software shortcut in gesture mode in accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture_talkback">Swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.</string>
+ <!-- Summary for software shortcut in accessibility edit shortcut dialog when user had enabled the accessibility floating button mode (Floating over other apps). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_dialog_summary_software_floating"><annotation id="link">Customize accessibility button</annotation></string>
<!-- Title for hardware shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_hardware">Hold volume keys</string>
<!-- Part of list to compose user's accessibility shortcut list. [CHAR LIMIT=NONE] -->
@@ -5164,6 +5182,26 @@
<string name="accessibility_shortcut_service_on_lock_screen_title">Shortcut from lock screen</string>
<!-- Description of accessibility shortcut. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_description">Allow feature shortcut to turn on from the lock screen. Hold both volume keys for a few seconds.</string>
+ <!-- Title for the accessibility button page. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_title">Accessibility button</string>
+ <!-- Summary text for the accessibility button preference. [CHAR LIMIT=50] -->
+ <string name="accessibility_button_summary">Quickly access accessibility features</string>
+ <!-- Description for the accessibility button page. Explain how this page works. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_button_description">Quickly access accessibility features from any screen. \n\nTo get started, go to accessibility settings and select a feature. Tap on the shortcut and select the accessibility button.</string>
+ <!-- Title for the location of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_location_title">Location</string>
+ <!-- Title for the size of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_size_title">Size</string>
+ <!-- Title for the fade of the accessibility button. [CHAR LIMIT=35] -->
+ <string name="accessibility_button_fade_title">Fade when not in use</string>
+ <!-- Summary for the fade of the accessibility button. [CHAR LIMIT=80] -->
+ <string name="accessibility_button_fade_summary">Fades after a few seconds so it\u2019s easier to see your screen</string>
+ <!-- Title for the transparency of the accessibility button. Will become fade when not interact with the accessibility button. [CHAR LIMIT=40] -->
+ <string name="accessibility_button_opacity_title">Transparency when not in use</string>
+ <!-- Label on the left side of transparency adjustment slider [CHAR LIMIT=30] -->
+ <string name="accessibility_button_low_label">Transparent</string>
+ <!-- Label on the right side of transparency adjustment slider [CHAR LIMIT=30] -->
+ <string name="accessibility_button_high_label">Non-transparent</string>
<!-- Title for the accessibility preference to high contrast text. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_high_text_contrast_preference_title">High contrast text</string>
<!-- Title for the accessibility preference to auto update screen magnification. [CHAR LIMIT=35] -->
@@ -5338,15 +5376,17 @@
<!-- Summary shown for tritanomaly (blue-yellow color blindness) [CHAR LIMIT=45] -->
<string name="daltonizer_mode_tritanomaly_summary">Blue-yellow</string>
- <!-- Title for the accessibility preference and switch of the Reduce Brightness feature. [CHAR LIMIT=NONE] -->
- <string name="reduce_bright_colors_preference_title">Reduce brightness</string>
+ <!-- Title for the accessibility preference of the Reduce Brightness feature. [CHAR LIMIT=NONE] -->
+ <string name="reduce_bright_colors_preference_title">Extra dim</string>
+ <!-- Title for the activation switch of the Reduce Brightness feature. [CHAR LIMIT=NONE] -->
+ <string name="reduce_bright_colors_switch_title">Make screen extra dim</string>
<!-- Summary for the accessibility preference to configure Reduce Brightness feature. [CHAR LIMIT=NONE] -->
- <string name="reduce_bright_colors_preference_summary" product="default">Make screen darker than your phone\u2019s minimum brightness</string>
+ <string name="reduce_bright_colors_preference_summary" product="default">Dim screen beyond your phone\u2019s minimum brightness</string>
<!-- Summary for the accessibility preference to configure Reduce Brightness feature. [CHAR LIMIT=NONE] -->
- <string name="reduce_bright_colors_preference_summary" product="tablet">Make screen darker than your tablet\u2019s minimum brightness</string>
+ <string name="reduce_bright_colors_preference_summary" product="tablet">Dim screen beyond your tablet\u2019s minimum brightness</string>
<!-- Subtitle that describes Reduce Brightness. [CHAR LIMIT=NONE] -->
<string name="reduce_bright_colors_preference_subtitle" product="default">
- <![CDATA[Make your screen darker so it\u2019s more comfortable to read.<br/><br/>
+ <![CDATA[Make your screen dimmer so it\u2019s more comfortable to read.<br/><br/>
This can be helpful when:
<ol>
<li>\u00a0Your phone\u2019s default minimum brightness is still too bright</li>
@@ -5366,10 +5406,6 @@
</string>
<!-- Title for setting the brightness intensity of the display using Reduce Brightness. [CHAR LIMIT=NONE] -->
<string name="reduce_bright_colors_intensity_preference_title">Intensity</string>
- <!-- Start label for setting the brightness intensity of the display using Reduce Brightness. [CHAR LIMIT=50] -->
- <string name="reduce_bright_colors_intensity_preference_start_label">Slightly darker</string>
- <!-- End label for setting the brightness intensity of the display using Reduce Brightness. [CHAR LIMIT=NONE] -->
- <string name="reduce_bright_colors_intensity_preference_end_label">Darkest</string>
<!-- Title for setting whether the Reduce Brightness activation state persists across reboots. [CHAR LIMIT=NONE] -->
<string name="reduce_bright_colors_persist_preference_title">Keep on after device restarts</string>
@@ -7335,6 +7371,8 @@
<string name="help_uri_apps_wifi_access" translatable="false"></string>
<!-- Help URI, manage apps that have access to all files [DO NOT TRANSLATE] -->
<string name="help_uri_manage_external_storage" translatable="false"></string>
+ <!-- Help URI, manage apps that can modify media files [DO NOT TRANSLATE] -->
+ <string name="help_uri_media_management_apps" translatable="false"></string>
<!-- Help URI, Storage [DO NOT TRANSLATE] -->
<string name="help_uri_storage" translatable="false"></string>
<!-- Help URI, Accessibility [DO NOT TRANSLATE] -->
@@ -8193,7 +8231,7 @@
<string name="zen_mode_settings_dnd_custom_settings_footer">Do Not Disturb is on for <xliff:g id="rule_names" example="Sleeping and Work">%s</xliff:g> with custom settings.</string>
<!-- [CHAR LIMIT=120] Zen mode settings footer: Link following zen_mode_settings_dnd_custom_settings_footer to see the currently applied custom dnd settings. -->
- <string name="zen_mode_settings_dnd_custom_settings_footer_link"><annotation id="link"> View custom settings</annotation></string>
+ <string name="zen_mode_settings_dnd_custom_settings_footer_link"> <annotation id="link">View custom settings</annotation></string>
<!--[CHAR LIMIT=40] Zen Interruption level: Priority. -->
<string name="zen_interruption_level_priority">Priority only</string>
@@ -8251,6 +8289,9 @@
<!-- Do not disturb settings, main screen, field, duration setting where user can specify how
long dnd will last when toggling dnd on from qs) [CHAR LIMIT=100] -->
<string name="zen_category_duration">Duration for Quick Settings</string>
+ <!-- Do not disturb settings, main screen, category header describing settings that do not
+ fit in another group [CHAR LIMIT=100] -->
+ <string name="zen_settings_general">General</string>
<!-- Do not disturb settings, sound and vibrations screen footer [CHAR LIMIT=NONE]-->
<string name="zen_sound_footer">When Do Not Disturb is on, sound and vibration will be muted, except for the items you allow above.</string>
@@ -9687,7 +9728,7 @@
<string name="app_launch_domain_links_title">Opening links</string>
<string name="app_launch_open_domain_urls_title">Open supported links</string>
<!-- Preference title for Supported links open in this app. [CHAR LIMIT=60] -->
- <string name="app_launch_top_intro_message">Supported links open in this app</string>
+ <string name="app_launch_top_intro_message">Allow web links to open in this app</string>
<!-- Preference title for Links to open in this app. [CHAR LIMIT=60] -->
<string name="app_launch_links_category">Links to open in this app</string>
@@ -10334,6 +10375,15 @@
<!-- Label for showing apps that can manage external storage[CHAR LIMIT=45] -->
<string name="filter_manage_external_storage">Can access all files</string>
+ <!-- Media management apps settings title [CHAR LIMIT=40] -->
+ <string name="media_management_apps_title">Media management apps</string>
+ <!-- Label for a setting which controls whether an app can manage media files [CHAR LIMIT=45] -->
+ <string name="media_management_apps_toggle_label">Allow app to manage media files</string>
+ <!-- Description for a setting which controls whether an app can manage media files [CHAR LIMIT=NONE] -->
+ <string name="media_management_apps_description">If allowed, this app can modify or delete media files on this device or connected storage device without asking you. App must have permission to access files and media.</string>
+ <!-- Search keywords for media management apps settings [CHAR_LIMIT=NONE] -->
+ <string name="keywords_media_management_apps">Media, File, Management, Manager, Manage, Edit, Editor, App, Application, Program</string>
+
<!-- Keyword for VR setting -->
<string name="keywords_vr_listener">vr virtual reality listener stereo helper service</string>
<!-- Main settings screen item's title to go into the overlay settings screen [CHAR LIMIT=30] -->
@@ -10522,6 +10572,9 @@
<string name="admin_device_owner_message">Your admin can monitor and manage apps and data
associated with this device, including settings, permissions, corporate access,
network activity, and the device\'s location information.</string>
+ <!-- Shown in admin details page to warn user about policies the admin can set on a financed device. [CHAR LIMIT=NONE] -->
+ <string name="admin_financed_message">Your device administrator may be able to access data
+ associated with this device, manage apps, and change this device\’s settings.</string>
<!-- Turn off a conditional state of the device (e.g. airplane mode, or hotspot) [CHAR LIMIT=30] -->
<string name="condition_turn_off">Turn off</string>
@@ -11522,6 +11575,10 @@
<!-- Follows the percent of storage used by a storage volume. Exposed inside of a donut graph. [CHAR LIMIT=7]-->
<string name="storage_percent_full">used</string>
+ <!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
+ <string name="storage_usage_summary"><xliff:g id="number" example="128">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g> used</string>
+ <!-- Summary of a single storage volume total space. [CHAR LIMIT=24] -->
+ <string name="storage_total_summary">Total <xliff:g id="number" example="128">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g></string>
<!-- Label for button allow user to remove the instant app from the device. -->
<string name="clear_instant_app_data">Clear app</string>
@@ -12824,7 +12881,9 @@
<!-- Title for battery saver main switch preferences. [CHAR LIMIT=50] -->
<string name="battery_saver_main_switch_title">Use battery saver</string>
<!-- Title for Do Not Disturb main switch preferences. [CHAR LIMIT=50] -->
- <string name="do_not_disturb_main_switch_title">Use Do Not Disturb</string>
+ <string name="do_not_disturb_main_switch_title_on">Turn off now</string>
+ <!-- Title for Do Not Disturb main switch preferences. [CHAR LIMIT=50] -->
+ <string name="do_not_disturb_main_switch_title_off">Turn on now</string>
<!-- Title for Night Light main switch preferences. [CHAR LIMIT=50] -->
<string name="night_light_main_switch_title">Use Night Light</string>
<!-- Title for NFC main switch preferences. [CHAR LIMIT=50] -->
@@ -12871,4 +12930,10 @@
<!-- Label for extra app info settings for a specific app [CHAR LIMIT=40] -->
<string name="extra_app_info_label" translatable="false"></string>
+
+ <!-- Title for toggle controlling whether notifications are shown when an app pastes from clipboard. [CHAR LIMIT=50] -->
+ <string name="show_clip_access_notification">Copy & paste notifications</string>
+
+ <!-- Summary for toggle controlling whether notifications are shown when an app pastes from clipboard. [CHAR LIMIT=NONE] -->
+ <string name="show_clip_access_notification_summary">Show a message when apps access text or data you have copied</string>
</resources>
diff --git a/res/xml/accessibility_button_settings.xml b/res/xml/accessibility_button_settings.xml
new file mode 100644
index 0000000..5e81616
--- /dev/null
+++ b/res/xml/accessibility_button_settings.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/accessibility_button_title">
+
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="caption_preview"
+ android:title="@string/summary_placeholder"
+ android:layout="@layout/accessibility_button_preview"
+ android:selectable="false"
+ settings:searchable="false"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.AccessibilityButtonPreviewPreferenceController"/>
+
+ <ListPreference
+ android:entries="@array/accessibility_button_location_selector_titles"
+ android:entryValues="@array/accessibility_button_location_selector_values"
+ android:key="accessibility_button_location"
+ android:title="@string/accessibility_button_location_title"
+ android:summary="%s"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.AccessibilityButtonLocationPreferenceController"/>
+
+ <ListPreference
+ android:entries="@array/accessibility_button_size_selector_titles"
+ android:entryValues="@array/accessibility_button_size_selector_values"
+ android:key="accessibility_button_size"
+ android:title="@string/accessibility_button_size_title"
+ android:summary="%s"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuSizePreferenceController"/>
+
+ <SwitchPreference
+ android:key="accessibility_button_fade"
+ android:title="@string/accessibility_button_fade_title"
+ android:summary="@string/accessibility_button_fade_summary"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuFadePreferenceController"/>
+
+ <com.android.settings.widget.SeekBarPreference
+ android:key="accessibility_button_opacity"
+ android:title="@string/accessibility_button_opacity_title"
+ android:selectable="true"
+ android:persistent="false"
+ settings:controller="com.android.settings.accessibility.FloatingMenuOpacityPreferenceController"/>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="accessibility_button_footer"
+ android:title="@string/accessibility_button_description"
+ android:selectable="false"
+ settings:searchable="false"
+ android:persistent="false"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/accessibility_shortcuts_settings.xml b/res/xml/accessibility_shortcuts_settings.xml
index 35314e7..465f96d 100644
--- a/res/xml/accessibility_shortcuts_settings.xml
+++ b/res/xml/accessibility_shortcuts_settings.xml
@@ -21,6 +21,13 @@
android:persistent="false"
android:title="@string/accessibility_shortcuts_settings_title">
+ <Preference
+ android:fragment="com.android.settings.accessibility.AccessibilityButtonFragment"
+ android:key="accessibility_button_preference"
+ android:persistent="false"
+ android:title="@string/accessibility_button_title"
+ android:summary="@string/accessibility_button_summary"/>
+
<SwitchPreference
android:key="accessibility_shortcut_preference"
android:persistent="false"
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 1646b1d..93f30e5 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -32,6 +32,7 @@
android:title="@string/location_recent_location_access_see_all"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.location.RecentLocationAccessSeeAllFragment"
+ settings:controller="com.android.settings.location.RecentLocationAccessSeeAllButtonPreferenceController"
settings:searchable="false"/>
<PreferenceCategory
diff --git a/res/xml/location_settings_personal.xml b/res/xml/location_settings_personal.xml
index 479c61f..0e971d6 100644
--- a/res/xml/location_settings_personal.xml
+++ b/res/xml/location_settings_personal.xml
@@ -33,6 +33,7 @@
android:title="@string/location_recent_location_access_see_all"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.location.RecentLocationAccessSeeAllFragment"
+ settings:controller="com.android.settings.location.RecentLocationAccessSeeAllButtonPreferenceController"
settings:searchable="false"/>
<!-- This preference category gets removed if new_recent_location_ui is disabled -->
diff --git a/res/xml/location_settings_workprofile.xml b/res/xml/location_settings_workprofile.xml
index 40a822c..c3efcbe 100644
--- a/res/xml/location_settings_workprofile.xml
+++ b/res/xml/location_settings_workprofile.xml
@@ -33,7 +33,7 @@
android:title="@string/location_recent_location_access_see_all"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.location.RecentLocationAccessSeeAllFragment"
- settings:controller="com.android.settings.core.WorkPreferenceController"
+ settings:controller="com.android.settings.location.RecentLocationAccessSeeAllButtonPreferenceController"
settings:forWork="true"
settings:searchable="false"/>
diff --git a/res/xml/media_management_apps.xml b/res/xml/media_management_apps.xml
new file mode 100644
index 0000000..7cf0d77
--- /dev/null
+++ b/res/xml/media_management_apps.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/media_management_apps_title">
+
+ <com.android.settings.widget.FilterTouchesSwitchPreference
+ android:key="media_management_apps_toggle"
+ android:title="@string/media_management_apps_toggle_label" />
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="media_management_apps_description"
+ android:title="@string/media_management_apps_description"
+ android:selectable="false" />
+
+</PreferenceScreen>
diff --git a/res/xml/network_and_internet.xml b/res/xml/network_and_internet.xml
index c92ce94..4a11cbe 100644
--- a/res/xml/network_and_internet.xml
+++ b/res/xml/network_and_internet.xml
@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="network_and_internet_screen"
- android:title="@string/network_dashboard_title"
- settings:initialExpandedChildrenCount="5">
+ android:title="@string/network_dashboard_title">
<PreferenceCategory
android:key="multi_network_header"
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index 035026b..bb7117d 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -18,8 +18,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="network_provider_and_internet_screen"
- android:title="@string/network_dashboard_title"
- settings:initialExpandedChildrenCount="5">
+ android:title="@string/network_dashboard_title">
<com.android.settingslib.RestrictedPreference
android:fragment="com.android.settings.network.NetworkProviderSettings"
diff --git a/res/xml/power_usage_summary.xml b/res/xml/power_usage_summary.xml
index 75bbca0..8c6fe41 100644
--- a/res/xml/power_usage_summary.xml
+++ b/res/xml/power_usage_summary.xml
@@ -40,32 +40,30 @@
android:title="@string/summary_placeholder"
settings:controller="com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController" />
- <PreferenceCategory>
- <SwitchPreference
- android:key="battery_percentage"
- android:title="@string/battery_percentage"
- android:summary="@string/battery_percentage_description"
- settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" />
- </PreferenceCategory>
+ <Preference
+ android:fragment="com.android.settings.fuelgauge.batterysaver.BatterySaverSettings"
+ android:key="battery_saver_summary"
+ android:title="@string/battery_saver"
+ app:iconSpaceReserved="false"
+ settings:controller="com.android.settings.fuelgauge.BatterySaverController" />
- <PreferenceCategory>
- <Preference
- android:fragment="com.android.settings.fuelgauge.batterysaver.BatterySaverSettings"
- android:key="battery_saver_summary"
- android:title="@string/battery_saver"
- settings:controller="com.android.settings.fuelgauge.BatterySaverController" />
+ <Preference
+ android:fragment="com.android.settings.fuelgauge.SmartBatterySettings"
+ android:key="smart_battery_manager"
+ android:title="@string/smart_battery_manager_title"
+ app:iconSpaceReserved="false"
+ settings:controller="com.android.settings.fuelgauge.batterytip.BatteryManagerPreferenceController" />
- <Preference
- android:fragment="com.android.settings.fuelgauge.SmartBatterySettings"
- android:key="smart_battery_manager"
- android:title="@string/smart_battery_manager_title"
- settings:controller="com.android.settings.fuelgauge.batterytip.BatteryManagerPreferenceController" />
- </PreferenceCategory>
+ <SwitchPreference
+ android:key="battery_percentage"
+ android:title="@string/battery_percentage"
+ android:summary="@string/battery_percentage_description"
+ app:iconSpaceReserved="false"
+ settings:controller="com.android.settings.display.BatteryPercentagePreferenceController" />
<com.android.settingslib.widget.FooterPreference
android:key="power_usage_footer"
android:title="@string/battery_footer_summary"
android:selectable="false"
- settings:searchable="false"
- settings:allowDividerAbove="true" />
+ settings:searchable="false" />
</PreferenceScreen>
diff --git a/res/xml/privacy_dashboard_settings.xml b/res/xml/privacy_dashboard_settings.xml
index f79da61..bd6a954 100644
--- a/res/xml/privacy_dashboard_settings.xml
+++ b/res/xml/privacy_dashboard_settings.xml
@@ -116,4 +116,11 @@
settings:controller="com.android.settings.privacy.EnableContentCaptureWithServiceSettingsPreferenceController">
</com.android.settings.widget.PrimarySwitchPreference>
+ <!-- Clipboard access notifications -->
+ <SwitchPreference
+ android:key="show_clip_access_notification"
+ android:title="@string/show_clip_access_notification"
+ android:summary="@string/show_clip_access_notification_summary"
+ settings:controller="com.android.settings.privacy.ShowClipAccessNotificationPreferenceController"/>
+
</PreferenceScreen>
diff --git a/res/xml/reduce_bright_colors_settings.xml b/res/xml/reduce_bright_colors_settings.xml
index 17e8b02..b9ca854 100644
--- a/res/xml/reduce_bright_colors_settings.xml
+++ b/res/xml/reduce_bright_colors_settings.xml
@@ -17,16 +17,13 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res-auto"
android:persistent="false"
android:title="@string/reduce_bright_colors_preference_title">
- <com.android.settings.widget.LabeledContinuousSeekBarPreference
+ <com.android.settings.widget.SeekBarPreference
android:key="rbc_intensity"
android:persistent="false"
- android:title="@string/reduce_bright_colors_intensity_preference_title"
- settings:textStart="@string/reduce_bright_colors_intensity_preference_start_label"
- settings:textEnd="@string/reduce_bright_colors_intensity_preference_end_label"/>
+ android:title="@string/reduce_bright_colors_intensity_preference_title"/>
<SwitchPreference
android:key="rbc_persist"
diff --git a/res/xml/special_access.xml b/res/xml/special_access.xml
index 671d882..83c23b5 100644
--- a/res/xml/special_access.xml
+++ b/res/xml/special_access.xml
@@ -61,6 +61,16 @@
settings:controller="com.android.settings.applications.specialaccess.zenaccess.ZenAccessController" />
<Preference
+ android:key="media_management_apps"
+ android:title="@string/media_management_apps_title"
+ android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
+ settings:keywords="@string/keywords_media_management_apps">
+ <extra
+ android:name="classname"
+ android:value="com.android.settings.Settings$MediaManagementAppsActivity" />
+ </Preference>
+
+ <Preference
android:key="write_settings_apps"
android:title="@string/write_settings"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml
index bc58d7e..b49228e 100644
--- a/res/xml/storage_dashboard_fragment.xml
+++ b/res/xml/storage_dashboard_fragment.xml
@@ -19,11 +19,22 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/storage_settings"
android:orderingFromXml="false">
- <com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference
- android:key="storage_summary"
- android:order="0"
+ <com.android.settingslib.widget.SettingsSpinnerPreference
+ android:key="storage_spinner"
+ android:order="-2"
settings:searchable="false"
- settings:controller="com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController"/>
+ settings:controller="com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController"/>
+ <com.android.settingslib.widget.UsageProgressBarPreference
+ android:key="storage_summary"
+ android:order="-1"
+ settings:searchable="false"
+ settings:controller="com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController"/>
+ <Preference
+ android:key="free_up_space"
+ android:order="0"
+ android:title="@string/storage_free_up_space_title"
+ android:summary="@string/storage_free_up_space_summary"
+ settings:allowDividerAbove="true"/>
<com.android.settings.widget.PrimarySwitchPreference
android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
android:key="toggle_asm"
@@ -74,4 +85,4 @@
android:key="pref_secondary_users"
android:title="@string/storage_other_users"
android:order="200" />
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/res/xml/top_level_settings_grouped.xml b/res/xml/top_level_settings_grouped.xml
index d6564b7..704a106 100644
--- a/res/xml/top_level_settings_grouped.xml
+++ b/res/xml/top_level_settings_grouped.xml
@@ -24,7 +24,7 @@
android:fragment="com.android.settings.network.NetworkDashboardFragment"
android:icon="@drawable/ic_homepage_network_v2"
android:key="top_level_network"
- android:order="-140"
+ android:order="-150"
android:title="@string/network_dashboard_title"
settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>
@@ -32,153 +32,128 @@
android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
android:icon="@drawable/ic_homepage_connected_device_v2"
android:key="top_level_connected_devices"
- android:order="-130"
+ android:order="-140"
android:title="@string/connected_devices_dashboard_title"
settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>
- <PreferenceCategory
- android:key="apps"
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.applications.AppDashboardFragment"
+ android:icon="@drawable/ic_homepage_apps_v2"
+ android:key="top_level_apps"
+ android:order="-130"
+ android:title="@string/apps_dashboard_title"/>
+
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.notification.ConfigureNotificationSettings"
+ android:icon="@drawable/ic_homepage_notification_v2"
+ android:key="top_level_notification"
android:order="-120"
- settings:allowDividerAbove="false">
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.applications.AppDashboardFragment"
- android:icon="@drawable/ic_homepage_apps_v2"
- android:key="top_level_apps"
- android:order="-120"
- android:title="@string/apps_dashboard_title"/>
+ android:title="@string/configure_notification_settings"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.notification.ConfigureNotificationSettings"
- android:icon="@drawable/ic_homepage_notification_v2"
- android:key="top_level_notification"
- android:order="-110"
- android:title="@string/configure_notification_settings"/>
- </PreferenceCategory>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
+ android:icon="@drawable/ic_homepage_battery_v2"
+ android:key="top_level_battery"
+ android:order="-110"
+ android:title="@string/power_usage_summary_title"
+ settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
- <PreferenceCategory
- android:key="phone_essential"
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.deviceinfo.StorageSettings"
+ android:icon="@drawable/ic_homepage_storage_v2"
+ android:key="top_level_storage"
android:order="-100"
- settings:allowDividerAbove="false">
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
- android:icon="@drawable/ic_homepage_battery_v2"
- android:key="top_level_battery"
- android:order="-100"
- android:title="@string/power_usage_summary_title"
- settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
+ android:title="@string/storage_settings"
+ settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.deviceinfo.StorageSettings"
- android:icon="@drawable/ic_homepage_storage_v2"
- android:key="top_level_storage"
- android:order="-90"
- android:title="@string/storage_settings"
- settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.notification.SoundSettings"
+ android:icon="@drawable/ic_homepage_sound_v2"
+ android:key="top_level_sound"
+ android:order="-90"
+ android:title="@string/sound_settings"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.notification.SoundSettings"
- android:icon="@drawable/ic_homepage_sound_v2"
- android:key="top_level_sound"
- android:order="-80"
- android:title="@string/sound_settings"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.DisplaySettings"
+ android:icon="@drawable/ic_homepage_display_v2"
+ android:key="top_level_display"
+ android:order="-80"
+ android:title="@string/display_settings"
+ settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.DisplaySettings"
- android:icon="@drawable/ic_homepage_display_v2"
- android:key="top_level_display"
- android:order="-70"
- android:title="@string/display_settings"
- settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>
+ <com.android.settings.homepage.RestrictedHomepagePreference
+ android:icon="@drawable/ic_homepage_wallpaper_v2"
+ android:key="top_level_wallpaper"
+ android:order="-70"
+ android:title="@string/wallpaper_settings_title"
+ settings:controller="com.android.settings.display.TopLevelWallpaperPreferenceController"/>
- <com.android.settings.homepage.RestrictedHomepagePreference
- android:icon="@drawable/ic_homepage_wallpaper_v2"
- android:key="top_level_wallpaper"
- android:order="-60"
- android:title="@string/wallpaper_settings_title"
- settings:controller="com.android.settings.display.TopLevelWallpaperPreferenceController"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.accessibility.AccessibilitySettings"
+ android:icon="@drawable/ic_homepage_accessibility_v2"
+ android:key="top_level_accessibility"
+ android:order="-60"
+ android:title="@string/accessibility_settings"
+ settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.accessibility.AccessibilitySettings"
- android:icon="@drawable/ic_homepage_accessibility_v2"
- android:key="top_level_accessibility"
- android:order="-50"
- android:title="@string/accessibility_settings"
- settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>
- </PreferenceCategory>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.security.SecuritySettings"
+ android:icon="@drawable/ic_homepage_security_v2"
+ android:key="top_level_security"
+ android:order="-50"
+ android:title="@string/security_settings_title"
+ settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>
- <PreferenceCategory
- android:key="privacy_and_security"
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"
+ android:icon="@drawable/ic_homepage_privacy_v2"
+ android:key="top_level_privacy"
android:order="-40"
- settings:allowDividerAbove="false">
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"
- android:icon="@drawable/ic_homepage_privacy_v2"
- android:key="top_level_privacy"
- android:order="-40"
- android:title="@string/privacy_dashboard_title"/>
+ android:title="@string/privacy_dashboard_title"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.location.LocationSettings"
- android:icon="@drawable/ic_homepage_location_v2"
- android:key="top_level_location"
- android:order="-30"
- android:title="@string/location_settings_title"
- settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.location.LocationSettings"
+ android:icon="@drawable/ic_homepage_location_v2"
+ android:key="top_level_location"
+ android:order="-30"
+ android:title="@string/location_settings_title"
+ settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.security.SecuritySettings"
- android:icon="@drawable/ic_homepage_security_v2"
- android:key="top_level_security"
- android:order="-20"
- android:title="@string/security_settings_title"
- settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:key="top_level_emergency"
+ android:title="@string/emergency_settings_preference_title"
+ android:icon="@drawable/ic_homepage_emergency_v2"
+ android:order="-20"
+ android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"/>
- <com.android.settings.homepage.HomepagePreference
- android:key="top_level_emergency"
- android:title="@string/emergency_settings_preference_title"
- android:icon="@drawable/ic_homepage_emergency_v2"
- android:order="-10"
- android:fragment="com.android.settings.emergency.EmergencyDashboardFragment"/>
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="accounts"
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.accounts.AccountDashboardFragment"
+ android:icon="@drawable/ic_homepage_accounts_v2"
+ android:key="top_level_accounts"
android:order="-10"
- settings:allowDividerAbove="false">
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.accounts.AccountDashboardFragment"
- android:icon="@drawable/ic_homepage_accounts_v2"
- android:key="top_level_accounts"
- android:order="-10"
- android:title="@string/account_dashboard_title"
- settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
- </PreferenceCategory>
+ android:title="@string/account_dashboard_title"
+ settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>
- <PreferenceCategory
- android:key="system"
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.system.SystemDashboardFragment"
+ android:icon="@drawable/ic_homepage_system_dashboard_v2"
+ android:key="top_level_system"
android:order="10"
- settings:allowDividerAbove="false">
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.system.SystemDashboardFragment"
- android:icon="@drawable/ic_homepage_system_dashboard_v2"
- android:key="top_level_system"
- android:order="10"
- android:title="@string/header_category_system"/>
+ android:title="@string/header_category_system"/>
- <com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
- android:icon="@drawable/ic_homepage_about_v2"
- android:key="top_level_about_device"
- android:order="20"
- android:title="@string/about_settings"
- settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>
+ <com.android.settings.homepage.HomepagePreference
+ android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
+ android:icon="@drawable/ic_homepage_about_v2"
+ android:key="top_level_about_device"
+ android:order="20"
+ android:title="@string/about_settings"
+ settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>
- <com.android.settings.homepage.HomepagePreference
- android:icon="@drawable/ic_homepage_support_v2"
- android:key="top_level_support"
- android:order="100"
- android:title="@string/page_tab_title_support"
- settings:controller="com.android.settings.support.SupportPreferenceController"/>
- </PreferenceCategory>
+ <com.android.settings.homepage.HomepagePreference
+ android:icon="@drawable/ic_homepage_support_v2"
+ android:key="top_level_support"
+ android:order="100"
+ android:title="@string/page_tab_title_support"
+ settings:controller="com.android.settings.support.SupportPreferenceController"/>
</PreferenceScreen>
diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml
index 34d8032..8648cff 100644
--- a/res/xml/wifi_tether_settings.xml
+++ b/res/xml/wifi_tether_settings.xml
@@ -37,12 +37,13 @@
android:persistent="false"
android:title="@string/wifi_hotspot_password_title"/>
- <ListPreference
- android:key="wifi_tether_network_ap_band"
- android:title="@string/wifi_hotspot_ap_band_title"/>
-
<SwitchPreference
android:key="wifi_tether_auto_turn_off"
android:title="@string/wifi_hotspot_auto_off_title"
android:summary="@string/wifi_hotspot_auto_off_summary"/>
+
+ <SwitchPreference
+ android:key="wifi_tether_maximize_compatibility"
+ android:title="@string/wifi_hotspot_maximize_compatibility"
+ android:summary="@string/wifi_hotspot_maximize_compatibility_summary"/>
</PreferenceScreen>
diff --git a/res/xml/zen_mode_settings.xml b/res/xml/zen_mode_settings.xml
index 78dee02..10b3e41 100644
--- a/res/xml/zen_mode_settings.xml
+++ b/res/xml/zen_mode_settings.xml
@@ -23,7 +23,6 @@
<!-- Turn on DND button -->
<com.android.settingslib.widget.MainSwitchPreference
android:key="zen_mode_toggle"
- android:title="@string/do_not_disturb_main_switch_title"
settings:keywords="@string/keywords_zen_mode_settings"/>
<PreferenceCategory
@@ -49,15 +48,18 @@
</PreferenceCategory>
<!-- Automatic rules -->
- <Preference
- android:key="zen_mode_automation_settings"
- android:title="@string/zen_category_schedule"
- settings:allowDividerAbove="true"
- android:fragment="com.android.settings.notification.zen.ZenModeAutomationSettings"/>
+ <PreferenceCategory
+ android:key="zen_mode_settings_schedule"
+ android:title="@string/zen_category_schedule">
+ <Preference
+ android:key="zen_mode_automation_settings"
+ android:title="@string/zen_category_schedule"
+ android:fragment="com.android.settings.notification.zen.ZenModeAutomationSettings"/>
+ </PreferenceCategory>
<PreferenceCategory
- android:key="zen_mode_settings_advanced"
- settings:initialExpandedChildrenCount="0">
+ android:title="@string/zen_settings_general"
+ android:key="zen_mode_settings_advanced">
<!-- DND duration settings -->
<com.android.settings.notification.zen.ZenDurationDialogPreference
diff --git a/src/com/android/settings/AirplaneModeEnabler.java b/src/com/android/settings/AirplaneModeEnabler.java
index 6028c18..735ae23 100644
--- a/src/com/android/settings/AirplaneModeEnabler.java
+++ b/src/com/android/settings/AirplaneModeEnabler.java
@@ -192,3 +192,4 @@
return WirelessUtils.isAirplaneModeOn(mContext);
}
}
+
diff --git a/src/com/android/settings/ProxySelector.java b/src/com/android/settings/ProxySelector.java
index a685841..57d3c6a 100644
--- a/src/com/android/settings/ProxySelector.java
+++ b/src/com/android/settings/ProxySelector.java
@@ -44,6 +44,8 @@
import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
import com.android.settings.core.InstrumentedFragment;
+import java.util.Arrays;
+
public class ProxySelector extends InstrumentedFragment implements DialogCreatable {
private static final String TAG = "ProxySelector";
@@ -229,7 +231,9 @@
return false;
}
}
- ProxyInfo p = new ProxyInfo(hostname, port, exclList);
+
+ ProxyInfo p = ProxyInfo.buildDirectProxy(
+ hostname, port, Arrays.asList(exclList.split(",")));
// FIXME: The best solution would be to make a better UI that would
// disable editing of the text boxes if the user chooses to use the
// default settings. i.e. checking a box to always use the default
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index cf43827..95e68de 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -217,6 +217,8 @@
public static class OverlaySettingsActivity extends SettingsActivity { /* empty */ }
public static class ManageExternalStorageActivity extends SettingsActivity { /* empty */ }
public static class AppManageExternalStorageActivity extends SettingsActivity { /* empty */ }
+ public static class MediaManagementAppsActivity extends SettingsActivity { /* empty */ }
+ public static class AppMediaManagementAppsActivity extends SettingsActivity { /* empty */ }
public static class WriteSettingsActivity extends SettingsActivity { /* empty */ }
public static class ChangeWifiStateActivity extends SettingsActivity { /* empty */ }
public static class AppDrawOverlaySettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsDumpService.java b/src/com/android/settings/SettingsDumpService.java
index 2b6c7d8..5e6ee93 100644
--- a/src/com/android/settings/SettingsDumpService.java
+++ b/src/com/android/settings/SettingsDumpService.java
@@ -14,13 +14,15 @@
package com.android.settings;
+import static android.content.pm.PackageManager.FEATURE_ETHERNET;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.net.ConnectivityManager;
import android.net.NetworkTemplate;
import android.net.Uri;
import android.os.IBinder;
@@ -101,10 +103,10 @@
private JSONObject dumpDataUsage() throws JSONException {
JSONObject obj = new JSONObject();
DataUsageController controller = new DataUsageController(this);
- ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
SubscriptionManager manager = this.getSystemService(SubscriptionManager.class);
TelephonyManager telephonyManager = this.getSystemService(TelephonyManager.class);
- if (connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+ final PackageManager packageManager = this.getPackageManager();
+ if (telephonyManager.isDataCapable()) {
JSONArray array = new JSONArray();
for (SubscriptionInfo info : manager.getAvailableSubscriptionInfoList()) {
telephonyManager = telephonyManager
@@ -117,10 +119,11 @@
}
obj.put("cell", array);
}
- if (connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_WIFI)) {
+ if (packageManager.hasSystemFeature(FEATURE_WIFI)) {
obj.put("wifi", dumpDataUsage(NetworkTemplate.buildTemplateWifiWildcard(), controller));
}
- if (connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_ETHERNET)) {
+
+ if (packageManager.hasSystemFeature(FEATURE_ETHERNET)) {
obj.put("ethernet", dumpDataUsage(NetworkTemplate.buildTemplateEthernet(), controller));
}
return obj;
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 4bf97cc..391ee90 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -54,6 +54,7 @@
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.ConnectivityManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.wifi.WifiManager;
@@ -111,7 +112,6 @@
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.widget.ActionBarShadowController;
-import java.net.InetAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
@@ -246,13 +246,13 @@
private static String formatIpAddresses(LinkProperties prop) {
if (prop == null) return null;
- final Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+ final Iterator<LinkAddress> iter = prop.getAllLinkAddresses().iterator();
// If there are no entries, return null
if (!iter.hasNext()) return null;
// Concatenate all available addresses, comma separated
String addresses = "";
while (iter.hasNext()) {
- addresses += iter.next().getHostAddress();
+ addresses += iter.next().getAddress().getHostAddress();
if (iter.hasNext()) addresses += "\n";
}
return addresses;
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonFragment.java b/src/com/android/settings/accessibility/AccessibilityButtonFragment.java
new file mode 100644
index 0000000..c3e683a
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonFragment.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/** Settings fragment containing accessibility button properties. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class AccessibilityButtonFragment extends DashboardFragment {
+
+ private static final String TAG = "AccessibilityButtonFragment";
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.accessibility_button_settings;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.ACCESSIBILITY_BUTTON_SETTINGS;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.accessibility_button_settings);
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
new file mode 100644
index 0000000..e9ed19a
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+import com.google.common.primitives.Ints;
+
+/** Preference controller that controls the preferred location in accessibility button page. */
+public class AccessibilityButtonLocationPreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
+ private int mDefaultLocation;
+
+ public AccessibilityButtonLocationPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isGestureNavigateEnabled(mContext)
+ ? DISABLED_DEPENDENT_SETTING : AVAILABLE;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(getCurrentAccessibilityButtonMode());
+ }
+
+ private String getCurrentAccessibilityButtonMode() {
+ final int mode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, mDefaultLocation);
+ return String.valueOf(mode);
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_location_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultLocation = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
new file mode 100644
index 0000000..69a7a46
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceController.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.LayoutPreference;
+
+/** Preference controller that controls the preview effect in accessibility button page. */
+public class AccessibilityButtonPreviewPreferenceController extends BasePreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ private static final int SMALL_SIZE = 0;
+ private static final float DEFAULT_OPACITY = 0.55f;
+ private static final int DEFAULT_SIZE = 0;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+ private FloatingMenuLayerDrawable mFloatingMenuPreviewDrawable;
+
+ @VisibleForTesting
+ ImageView mPreview;
+
+ public AccessibilityButtonPreviewPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updatePreviewPreference();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final LayoutPreference preference = screen.findPreference(getPreferenceKey());
+ mPreview = preference.findViewById(R.id.preview_image);
+
+ updatePreviewPreference();
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updatePreviewPreference() {
+ if (AccessibilityUtil.isFloatingMenuEnabled(mContext)) {
+ final int size = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, DEFAULT_SIZE);
+ final int opacity = (int) (Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY) * 100);
+ final int floatingMenuIconId = (size == SMALL_SIZE)
+ ? R.drawable.accessibility_button_preview_small_floating_menu
+ : R.drawable.accessibility_button_preview_large_floating_menu;
+
+ mPreview.setImageDrawable(getFloatingMenuPreviewDrawable(floatingMenuIconId, opacity));
+ // Only change opacity(alpha) would not invoke redraw view, need to invalidate manually.
+ mPreview.invalidate();
+ } else {
+ mPreview.setImageDrawable(
+ mContext.getDrawable(R.drawable.accessibility_button_navigation));
+ }
+ }
+
+ private Drawable getFloatingMenuPreviewDrawable(int resId, int opacity) {
+ if (mFloatingMenuPreviewDrawable == null) {
+ mFloatingMenuPreviewDrawable = FloatingMenuLayerDrawable.createLayerDrawable(
+ mContext, resId, opacity);
+ } else {
+ mFloatingMenuPreviewDrawable.updateLayerDrawable(mContext, resId, opacity);
+ }
+
+ return mFloatingMenuPreviewDrawable;
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index f349a12..5d2a3fa 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -16,7 +16,10 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.ItemInfoArrayAdapter.ItemInfo;
+
import android.app.Dialog;
+import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
@@ -24,25 +27,35 @@
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
import android.text.style.ImageSpan;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.utils.AnnotationSpan;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
/**
* Utility class for creating the edit dialog.
@@ -57,15 +70,13 @@
@IntDef({
DialogType.EDIT_SHORTCUT_GENERIC,
DialogType.EDIT_SHORTCUT_MAGNIFICATION,
- DialogType.EDIT_MAGNIFICATION_MODE,
DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT,
})
private @interface DialogType {
int EDIT_SHORTCUT_GENERIC = 0;
int EDIT_SHORTCUT_MAGNIFICATION = 1;
- int EDIT_MAGNIFICATION_MODE = 2;
- int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 3;
+ int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 2;
}
/**
@@ -103,23 +114,6 @@
}
/**
- * Method to show the magnification mode dialog in Magnification.
- *
- * @param context A valid context
- * @param dialogTitle The title of magnify mode dialog
- * @param listener The listener to determine the action of magnify mode dialog
- * @return A magnification mode dialog in Magnification
- */
- public static AlertDialog showMagnificationModeDialog(Context context,
- CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
- final AlertDialog alertDialog = createDialog(context,
- DialogType.EDIT_MAGNIFICATION_MODE, dialogTitle, listener);
- alertDialog.show();
- setScrollIndicators(alertDialog);
- return alertDialog;
- }
-
- /**
* Method to show the magnification edit shortcut dialog in Magnification.
*
* @param context A valid context
@@ -159,11 +153,21 @@
*/
private static void setScrollIndicators(AlertDialog dialog) {
final ScrollView scrollView = dialog.findViewById(R.id.container_layout);
- scrollView.setScrollIndicators(
+ setScrollIndicators(scrollView);
+ }
+
+ /**
+ * Sets the scroll indicators for dialog view. The indicators appear while content view is
+ * out of vision for vertical scrolling.
+ *
+ * @param view The view contains customized dialog content. Usually it is {@link ScrollView} or
+ * {@link AbsListView}
+ */
+ private static void setScrollIndicators(@NonNull View view) {
+ view.setScrollIndicators(
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
}
-
private static void setEditShortcutButtonsListener(AlertDialog dialog,
View.OnClickListener listener) {
final View contentView = dialog.findViewById(R.id.container_layout);
@@ -208,12 +212,6 @@
initMagnifyShortcut(context, contentView);
initAdvancedWidget(contentView);
break;
- case DialogType.EDIT_MAGNIFICATION_MODE:
- contentView = inflater.inflate(
- R.layout.accessibility_edit_magnification_mode, null);
- initMagnifyFullScreen(context, contentView);
- initMagnifyWindowScreen(context, contentView);
- break;
case DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT:
contentView = inflater.inflate(
R.layout.accessibility_edit_magnification_shortcut, null);
@@ -225,25 +223,6 @@
return contentView;
}
- private static void initMagnifyFullScreen(Context context, View view) {
- final View dialogView = view.findViewById(R.id.magnify_full_screen);
- final CharSequence title = context.getText(
- R.string.accessibility_magnification_area_settings_full_screen);
- setupShortcutWidget(dialogView, title, R.drawable.accessibility_magnification_full_screen);
- }
-
- private static void initMagnifyWindowScreen(Context context, View view) {
- final View dialogView = view.findViewById(R.id.magnify_window_screen);
- final CharSequence title = context.getText(
- R.string.accessibility_magnification_area_settings_window_screen);
- setupShortcutWidget(dialogView, title,
- R.drawable.accessibility_magnification_window_screen);
- }
-
- private static void setupShortcutWidget(View view, CharSequence titleText, int imageResId) {
- setupShortcutWidget(view, titleText, null, imageResId);
- }
-
private static void setupShortcutWidget(View view, CharSequence titleText,
CharSequence summaryText, int imageResId) {
final CheckBox checkBox = view.findViewById(R.id.checkbox);
@@ -253,6 +232,8 @@
summary.setVisibility(View.GONE);
} else {
summary.setText(summaryText);
+ summary.setMovementMethod(LinkMovementMethod.getInstance());
+ summary.setFocusable(false);
}
final ImageView image = view.findViewById(R.id.image);
image.setImageResource(imageResId);
@@ -260,10 +241,13 @@
private static void initSoftwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
+ final CharSequence title = context.getText(
+ R.string.accessibility_shortcut_edit_dialog_title_software);
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
- setupShortcutWidget(dialogView, retrieveTitle(context),
- retrieveSummary(context, lineHeight), retrieveImageResId(context));
+
+ setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
+ retrieveImageResId(context));
}
private static void initHardwareShortcut(Context context, View view) {
@@ -297,35 +281,28 @@
});
}
- private static CharSequence retrieveTitle(Context context) {
- int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- return context.getText(resId);
- }
-
private static CharSequence retrieveSummary(Context context, int lineHeight) {
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
- return context.getText(resId);
- }
- return getSummaryStringWithIcon(context, lineHeight);
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? getSummaryStringWithLink(context) : getSummaryStringWithIcon(context, lineHeight);
}
private static int retrieveImageResId(Context context) {
- // TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
- return resId;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
+ }
+
+ private static CharSequence getSummaryStringWithLink(Context context) {
+ final View.OnClickListener linkListener = v -> new SubSettingLauncher(context)
+ .setDestination(AccessibilityButtonFragment.class.getName())
+ .setSourceMetricsCategory(
+ SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS)
+ .launch();
+ final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
+ AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, linkListener);
+
+ return AnnotationSpan.linkify(context.getText(
+ R.string.accessibility_shortcut_edit_dialog_summary_software_floating), linkInfo);
}
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
@@ -343,7 +320,6 @@
spannableMessage.setSpan(
imageSpan, indexIconStart, indexIconEnd,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-
return spannableMessage;
}
@@ -366,4 +342,52 @@
typedArray.recycle();
return colorResId;
}
+
+ /**
+ * Creates a dialog with the given view.
+ *
+ * @param context A valid context
+ * @param dialogTitle The title of the dialog
+ * @param customView The customized view
+ * @param listener This listener will be invoked when the positive button in the dialog is
+ * clicked
+ * @return the {@link Dialog} with the given view
+ */
+ public static Dialog createCustomDialog(Context context, CharSequence dialogTitle,
+ View customView, DialogInterface.OnClickListener listener) {
+ final AlertDialog alertDialog = new AlertDialog.Builder(context)
+ .setView(customView)
+ .setTitle(dialogTitle)
+ .setCancelable(true)
+ .setPositiveButton(R.string.save, listener)
+ .setNegativeButton(R.string.cancel, null)
+ .create();
+ if (customView instanceof ScrollView || customView instanceof AbsListView) {
+ setScrollIndicators(customView);
+ }
+ return alertDialog;
+ }
+
+ /**
+ * Creates a single choice {@link ListView} with given {@link ItemInfo} list.
+ *
+ * @param context A context.
+ * @param itemInfoList A {@link ItemInfo} list.
+ * @param itemListener The listener will be invoked when the item is clicked.
+ */
+ @NonNull
+ public static ListView createSingleChoiceListView(@NonNull Context context,
+ @NonNull List<? extends ItemInfo> itemInfoList,
+ @Nullable AdapterView.OnItemClickListener itemListener) {
+ final ListView list = new ListView(context);
+ // Set an id to save its state.
+ list.setId(android.R.id.list);
+ list.setDivider(/* divider= */ null);
+ list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ final ItemInfoArrayAdapter
+ adapter = new ItemInfoArrayAdapter(context, itemInfoList);
+ list.setAdapter(adapter);
+ list.setOnItemClickListener(itemListener);
+ return list;
+ }
}
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 482822e..5ea5462 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -333,7 +333,8 @@
}
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
- final CharSequence title = getSoftwareTitle(context);
+ final CharSequence title = context.getText(
+ R.string.accessibility_tutorial_dialog_title_button);
final ImageView image = createSoftwareImage(context);
final CharSequence instruction = getSoftwareInstruction(context);
final ImageView indicatorIcon =
@@ -390,44 +391,19 @@
return tutorialPages;
}
- private static CharSequence getSoftwareTitle(Context context) {
- final boolean isGestureNavigationEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final int resId = isGestureNavigationEnabled
- ? R.string.accessibility_tutorial_dialog_title_gesture
- : R.string.accessibility_tutorial_dialog_title_button;
-
- return context.getText(resId);
- }
-
private static ImageView createSoftwareImage(Context context) {
- int resId = R.drawable.accessibility_shortcut_type_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
- : R.drawable.accessibility_shortcut_type_software_gesture;
- }
+ final int resId = AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_floating
+ : R.drawable.accessibility_shortcut_type_software;
return createImageView(context, resId);
}
private static CharSequence getSoftwareInstruction(Context context) {
- final boolean isGestureNavigateEnabled =
- AccessibilityUtil.isGestureNavigateEnabled(context);
- final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
- int resId = R.string.accessibility_tutorial_dialog_message_button;
- if (isGestureNavigateEnabled) {
- resId = isTouchExploreEnabled
- ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
- : R.string.accessibility_tutorial_dialog_message_gesture;
- }
-
- CharSequence text = context.getText(resId);
- if (resId == R.string.accessibility_tutorial_dialog_message_button) {
- text = getSoftwareInstructionWithIcon(context, text);
- }
-
- return text;
+ return AccessibilityUtil.isFloatingMenuEnabled(context)
+ ? context.getText(R.string.accessibility_tutorial_dialog_message_floating_button)
+ : getSoftwareInstructionWithIcon(context,
+ context.getText(R.string.accessibility_tutorial_dialog_message_button));
}
private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java
index d510ac7..6b629ac 100644
--- a/src/com/android/settings/accessibility/AccessibilitySettings.java
+++ b/src/com/android/settings/accessibility/AccessibilitySettings.java
@@ -16,7 +16,7 @@
package com.android.settings.accessibility;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.AccessibilityShortcutInfo;
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index f547209..5c316a4 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -143,6 +144,13 @@
== NAV_BAR_MODE_GESTURAL;
}
+ /** Determines if a accessibility floating menu is being used. */
+ public static boolean isFloatingMenuEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+ }
+
/** Determines if a touch explore is being used. */
public static boolean isTouchExploreEnabled(Context context) {
final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
diff --git a/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
new file mode 100644
index 0000000..dd419d0
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuFadePreferenceController.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the fade switch button in accessibility button page. */
+public class FloatingMenuFadePreferenceController extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SwitchPreference mPreference;
+
+ public FloatingMenuFadePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isEnabled = (boolean) newValue;
+ putFloatingMenuFadeValue(isEnabled);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final SwitchPreference switchPreference = (SwitchPreference) preference;
+
+ switchPreference.setChecked(getFloatingMenuFadeValue() == ON);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private int getFloatingMenuFadeValue() {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, ON);
+ }
+
+ private void putFloatingMenuFadeValue(boolean isEnabled) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ isEnabled ? ON : OFF);
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
new file mode 100644
index 0000000..bfce114
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuLayerDrawable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+
+import java.util.Objects;
+
+/** LayerDrawable that contains device icon as background and floating menu icon as foreground. */
+public class FloatingMenuLayerDrawable extends LayerDrawable {
+
+ private FloatingMenuLayerDrawableState mState;
+
+ /**
+ * Creates a new layer drawable with the list of specified layers.
+ *
+ * @param layers a list of drawables to use as layers in this new drawable,
+ * must be non-null
+ */
+ private FloatingMenuLayerDrawable(@NonNull Drawable[] layers) {
+ super(layers);
+ }
+
+ /**
+ * Create the {@link LayerDrawable} that contains device icon as background and floating menu
+ * icon with given {@code opacity} value as foreground.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ * @return the drawable that combines the device icon and the floating menu icon
+ */
+ public static FloatingMenuLayerDrawable createLayerDrawable(Context context, int resId,
+ int opacity) {
+ final Drawable bg = context.getDrawable(R.drawable.accessibility_button_preview_base);
+ final FloatingMenuLayerDrawable basicDrawable = new FloatingMenuLayerDrawable(
+ new Drawable[]{bg, null});
+
+ basicDrawable.updateLayerDrawable(context, resId, opacity);
+ return basicDrawable;
+ }
+
+ /**
+ * Update the drawable with given {@code resId} drawable and {@code opacity}(alpha)
+ * value at index 1 layer.
+ *
+ * @param context the valid context used to get the icon
+ * @param resId the resource ID of the floating menu icon
+ * @param opacity the opacity to apply to the given icon
+ */
+ public void updateLayerDrawable(Context context, int resId, int opacity) {
+ final Drawable icon = context.getDrawable(resId);
+ icon.setAlpha(opacity);
+ this.setDrawable(/* index= */ 1, icon);
+ this.setConstantState(context, resId, opacity);
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ /** Stores the constant state and data to the given drawable. */
+ private void setConstantState(Context context, int resId, int opacity) {
+ mState = new FloatingMenuLayerDrawableState(context, resId, opacity);
+ }
+
+ /** {@link ConstantState} to store the data of {@link FloatingMenuLayerDrawable}. */
+ @VisibleForTesting
+ static class FloatingMenuLayerDrawableState extends ConstantState {
+
+ private final Context mContext;
+ private final int mResId;
+ private final int mOpacity;
+
+ FloatingMenuLayerDrawableState(Context context, int resId, int opacity) {
+ mContext = context;
+ mResId = resId;
+ mOpacity = opacity;
+ }
+
+ @NonNull
+ @Override
+ public Drawable newDrawable() {
+ return createLayerDrawable(mContext, mResId, mOpacity);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final FloatingMenuLayerDrawableState that = (FloatingMenuLayerDrawableState) o;
+ return mResId == that.mResId
+ && mOpacity == that.mOpacity
+ && Objects.equals(mContext, that.mContext);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mContext, mResId, mOpacity);
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
new file mode 100644
index 0000000..fea6fb6
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceController.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.SliderPreferenceController;
+import com.android.settings.widget.SeekBarPreference;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+/** Preference controller that controls the opacity seekbar in accessibility button page. */
+public class FloatingMenuOpacityPreferenceController extends SliderPreferenceController
+ implements LifecycleObserver, OnResume, OnPause {
+
+ @VisibleForTesting
+ static final float DEFAULT_OPACITY = 0.55f;
+ private static final int FADE_ENABLED = 1;
+ private static final float MIN_PROGRESS = 10f;
+ private static final float MAX_PROGRESS = 100f;
+ @VisibleForTesting
+ static final float PRECISION = 100f;
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ SeekBarPreference mPreference;
+
+ public FloatingMenuOpacityPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ mPreference.setContinuousUpdates(true);
+ mPreference.setMax(getMax());
+ mPreference.setMin(getMin());
+ mPreference.setHapticFeedbackMode(SeekBarPreference.HAPTIC_FEEDBACK_MODE_ON_ENDS);
+
+ updateState(mPreference);
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ /* notifyForDescendants= */ false, mContentObserver);
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ @Override
+ public int getSliderPosition() {
+ return convertOpacityFloatToInt(getOpacity());
+ }
+
+ @Override
+ public boolean setSliderPosition(int position) {
+ final float value = convertOpacityIntToFloat(position);
+
+ return Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, value);
+ }
+
+ @Override
+ public int getMax() {
+ return (int) MAX_PROGRESS;
+ }
+
+ @Override
+ public int getMin() {
+ return (int) MIN_PROGRESS;
+ }
+
+ private void updateAvailabilityStatus() {
+ final boolean fadeEnabled = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, FADE_ENABLED)
+ == FADE_ENABLED;
+
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext) && fadeEnabled);
+ }
+
+ private int convertOpacityFloatToInt(float value) {
+ return Math.round(value * PRECISION);
+ }
+
+ private float convertOpacityIntToFloat(int value) {
+ return (float) value / PRECISION;
+ }
+
+ private float getOpacity() {
+ float value = Settings.Secure.getFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY);
+ final float minValue = MIN_PROGRESS / PRECISION;
+ final float maxValue = MAX_PROGRESS / PRECISION;
+
+ return (value < minValue || value > maxValue) ? DEFAULT_OPACITY : value;
+ }
+}
+
diff --git a/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
new file mode 100644
index 0000000..2f0f833
--- /dev/null
+++ b/src/com/android/settings/accessibility/FloatingMenuSizePreferenceController.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+
+import com.google.common.primitives.Ints;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Preference controller that controls the preferred size in accessibility button page. */
+public class FloatingMenuSizePreferenceController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
+
+ private final ContentResolver mContentResolver;
+ @VisibleForTesting
+ final ContentObserver mContentObserver;
+
+ @VisibleForTesting
+ ListPreference mPreference;
+
+ private final ArrayMap<String, String> mValueTitleMap = new ArrayMap<>();
+ private int mDefaultSize;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ Size.SMALL,
+ Size.LARGE,
+ })
+ @VisibleForTesting
+ @interface Size {
+ int SMALL = 0;
+ int LARGE = 1;
+ }
+
+ public FloatingMenuSizePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAvailabilityStatus();
+ }
+ };
+
+ initValueTitleMap();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AccessibilityUtil.isFloatingMenuEnabled(mContext)
+ ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final ListPreference listPreference = (ListPreference) preference;
+ final Integer value = Ints.tryParse((String) newValue);
+ if (value != null) {
+ putAccessibilityFloatingMenuSize(value);
+ updateState(listPreference);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ final ListPreference listPreference = (ListPreference) preference;
+
+ listPreference.setValue(String.valueOf(getAccessibilityFloatingMenuSize(mDefaultSize)));
+ }
+
+ @Override
+ public void onResume() {
+ mContentResolver.registerContentObserver(
+ Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE), /* notifyForDescendants= */
+ false, mContentObserver);
+
+ }
+
+ @Override
+ public void onPause() {
+ mContentResolver.unregisterContentObserver(mContentObserver);
+ }
+
+ private void updateAvailabilityStatus() {
+ mPreference.setEnabled(AccessibilityUtil.isFloatingMenuEnabled(mContext));
+ }
+
+ private void initValueTitleMap() {
+ if (mValueTitleMap.size() == 0) {
+ final String[] values = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_values);
+ final String[] titles = mContext.getResources().getStringArray(
+ R.array.accessibility_button_size_selector_titles);
+ final int mapSize = values.length;
+
+ mDefaultSize = Integer.parseInt(values[0]);
+ for (int i = 0; i < mapSize; i++) {
+ mValueTitleMap.put(values[i], titles[i]);
+ }
+ }
+ }
+
+ @Size
+ private int getAccessibilityFloatingMenuSize(@Size int defaultValue) {
+ return Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultValue);
+ }
+
+ private void putAccessibilityFloatingMenuSize(@Size int value) {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, value);
+ }
+}
diff --git a/src/com/android/settings/accessibility/ItemInfoArrayAdapter.java b/src/com/android/settings/accessibility/ItemInfoArrayAdapter.java
new file mode 100644
index 0000000..edb16a2
--- /dev/null
+++ b/src/com/android/settings/accessibility/ItemInfoArrayAdapter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+
+import java.util.List;
+
+/**
+ * An {@link ArrayAdapter} to fill the information of {@link ItemInfo} in the item view. The item
+ * view must have textview to set the title.
+ *
+ * @param <T> the type of elements in the array, inherited from {@link ItemInfo}.
+ */
+public class ItemInfoArrayAdapter<T extends ItemInfoArrayAdapter.ItemInfo> extends ArrayAdapter<T> {
+
+ public ItemInfoArrayAdapter(@NonNull Context context, @NonNull List<T> items) {
+ super(context, R.layout.dialog_single_radio_choice_list_item, R.id.title, items);
+ }
+
+ @NonNull
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ final View root = super.getView(position, convertView, parent);
+
+ final ItemInfo item = getItem(position);
+ final TextView title = root.findViewById(R.id.title);
+ title.setText(item.mTitle);
+ final TextView summary = root.findViewById(R.id.summary);
+ if (!TextUtils.isEmpty(item.mSummary)) {
+ summary.setVisibility(View.VISIBLE);
+ summary.setText(item.mSummary);
+ } else {
+ summary.setVisibility(View.GONE);
+ }
+ final ImageView image = root.findViewById(R.id.image);
+ image.setImageResource(item.mDrawableId);
+ return root;
+ }
+
+ /**
+ * Presents a data structure shown in the item view.
+ */
+ public static class ItemInfo {
+ @NonNull
+ public final CharSequence mTitle;
+ @Nullable
+ public final CharSequence mSummary;
+ @DrawableRes
+ public final int mDrawableId;
+
+ public ItemInfo(@NonNull CharSequence title, @Nullable CharSequence summary,
+ @DrawableRes int drawableId) {
+ mTitle = title;
+ mSummary = summary;
+ mDrawableId = drawableId;
+ }
+ }
+}
diff --git a/src/com/android/settings/accessibility/MagnificationSettingsFragment.java b/src/com/android/settings/accessibility/MagnificationSettingsFragment.java
index 6003a6d..c4d6fd5 100644
--- a/src/com/android/settings/accessibility/MagnificationSettingsFragment.java
+++ b/src/com/android/settings/accessibility/MagnificationSettingsFragment.java
@@ -26,20 +26,26 @@
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CheckBox;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
+import java.util.ArrayList;
+import java.util.List;
import java.util.StringJoiner;
/** Settings page for magnification. */
@@ -52,6 +58,7 @@
static final int DIALOG_MAGNIFICATION_CAPABILITY = 1;
@VisibleForTesting
static final int DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = 2;
+
@VisibleForTesting
static final String EXTRA_CAPABILITY = "capability";
private static final int NONE = 0;
@@ -60,13 +67,13 @@
private Preference mModePreference;
@VisibleForTesting
Dialog mDialog;
- @VisibleForTesting
- CheckBox mMagnifyFullScreenCheckBox;
- @VisibleForTesting
- CheckBox mMagnifyWindowCheckBox;
+ @VisibleForTesting
+ ListView mMagnificationModesListView;
private int mCapabilities = NONE;
+ private final List<MagnificationModeInfo> mModeInfos = new ArrayList<>();
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -77,6 +84,7 @@
if (mCapabilities == NONE) {
mCapabilities = MagnificationCapabilities.getCapabilities(getPrefContext());
}
+ initModeInfos();
}
@Override
@@ -121,13 +129,10 @@
@Override
public Dialog onCreateDialog(int dialogId) {
final CharSequence title;
+
switch (dialogId) {
case DIALOG_MAGNIFICATION_CAPABILITY:
- title = getPrefContext().getString(
- R.string.accessibility_magnification_mode_title);
- mDialog = AccessibilityEditDialogUtils.showMagnificationModeDialog(getPrefContext(),
- title, this::callOnAlertDialogCheckboxClicked);
- initializeDialogCheckBox(mDialog);
+ mDialog = createMagnificationModeDialog();
return mDialog;
case DIALOG_MAGNIFICATION_SWITCH_SHORTCUT:
title = getPrefContext().getString(
@@ -136,10 +141,97 @@
getPrefContext(), title, this::onSwitchShortcutDialogPositiveButtonClicked);
return mDialog;
}
-
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
+ private Dialog createMagnificationModeDialog() {
+ mMagnificationModesListView = AccessibilityEditDialogUtils.createSingleChoiceListView(
+ getPrefContext(), mModeInfos, this::onMagnificationModeSelected);
+
+ final View headerView = LayoutInflater.from(getPrefContext()).inflate(
+ R.layout.accessibility_magnification_mode_header, mMagnificationModesListView,
+ false);
+ mMagnificationModesListView.addHeaderView(headerView, null, /* isSelectable= */false);
+
+ mMagnificationModesListView.setItemChecked(computeSelectedMagnificationModeIndex(), true);
+ final CharSequence title = getPrefContext().getString(
+ R.string.accessibility_magnification_mode_dialog_title);
+
+ return AccessibilityEditDialogUtils.createCustomDialog(getPrefContext(), title,
+ mMagnificationModesListView, this::onMagnificationModeDialogPositiveButtonClicked);
+ }
+
+ private int computeSelectedMagnificationModeIndex() {
+ final int size = mModeInfos.size();
+ for (int i = 0; i < size; i++) {
+ if (mModeInfos.get(i).mMagnificationMode == mCapabilities) {
+ return i + mMagnificationModesListView.getHeaderViewsCount();
+ }
+ }
+ Log.w(TAG, "chosen mode" + mCapabilities + "is not in the list");
+ return 0;
+ }
+
+ private void onMagnificationModeSelected(AdapterView<?> parent, View view, int position,
+ long id) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(position);
+ if (modeInfo.mMagnificationMode == mCapabilities) {
+ return;
+ }
+ mCapabilities = modeInfo.mMagnificationMode;
+ if (isTripleTapEnabled() && mCapabilities != MagnificationMode.FULLSCREEN) {
+ showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+ }
+
+ private void onMagnificationModeDialogPositiveButtonClicked(DialogInterface dialogInterface,
+ int which) {
+ final int selectedIndex = mMagnificationModesListView.getCheckedItemPosition();
+ if (selectedIndex != AdapterView.INVALID_POSITION) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) mMagnificationModesListView.getItemAtPosition(
+ selectedIndex);
+ updateCapabilities(modeInfo.mMagnificationMode);
+ } else {
+ Log.w(TAG, "no checked item in the list");
+ }
+ }
+
+ private void updateCapabilities(int mode) {
+ mCapabilities = mode;
+ MagnificationCapabilities.setCapabilities(getPrefContext(), mCapabilities);
+ mModePreference.setSummary(
+ MagnificationCapabilities.getSummary(getPrefContext(), mCapabilities));
+ }
+
+ private void initModeInfos() {
+ mModeInfos.clear();
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_full_screen), null,
+ R.drawable.accessibility_magnification_full_screen, MagnificationMode.FULLSCREEN));
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_window), null,
+ R.drawable.accessibility_magnification_window_screen, MagnificationMode.WINDOW));
+ mModeInfos.add(new MagnificationModeInfo(getPrefContext().getText(
+ R.string.accessibility_magnification_mode_dialog_option_switch),
+ getPrefContext().getText(
+ R.string.accessibility_magnification_area_settings_mode_switch_summary),
+ R.drawable.accessibility_magnification_switch, MagnificationMode.ALL));
+ }
+
+ @VisibleForTesting
+ static class MagnificationModeInfo extends ItemInfoArrayAdapter.ItemInfo {
+ @MagnificationMode
+ public final int mMagnificationMode;
+
+ MagnificationModeInfo(@NonNull CharSequence title, @Nullable CharSequence summary,
+ @DrawableRes int drawableId, @MagnificationMode int magnificationMode) {
+ super(title, summary, drawableId);
+ mMagnificationMode = magnificationMode;
+ }
+ }
+
private void initModePreference() {
mModePreference = findPreference(PREF_KEY_MODE);
mModePreference.setOnPreferenceClickListener(preference -> {
@@ -149,12 +241,6 @@
});
}
- private void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
- updateCapabilities(true);
- mModePreference.setSummary(
- MagnificationCapabilities.getSummary(getPrefContext(), mCapabilities));
- }
-
private void onSwitchShortcutDialogPositiveButtonClicked(View view) {
//TODO(b/147990389): Merge this function into util until magnification change format to
// Component.
@@ -188,95 +274,6 @@
joiner.toString());
}
- private void initializeDialogCheckBox(Dialog dialog) {
- final View dialogFullScreenView = dialog.findViewById(R.id.magnify_full_screen);
- final View dialogFullScreenTextArea = dialogFullScreenView.findViewById(R.id.container);
- mMagnifyFullScreenCheckBox = dialogFullScreenView.findViewById(R.id.checkbox);
-
- final View dialogWidowView = dialog.findViewById(R.id.magnify_window_screen);
- final View dialogWindowTextArea = dialogWidowView.findViewById(R.id.container);
- mMagnifyWindowCheckBox = dialogWidowView.findViewById(R.id.checkbox);
-
- updateAlertDialogCheckState();
- updateAlertDialogEnableState(dialogFullScreenTextArea, dialogWindowTextArea);
-
- setTextAreasClickListener(dialogFullScreenTextArea, mMagnifyFullScreenCheckBox,
- dialogWindowTextArea, mMagnifyWindowCheckBox);
- }
-
- private void setTextAreasClickListener(View fullScreenTextArea, CheckBox fullScreenCheckBox,
- View windowTextArea, CheckBox windowCheckBox) {
- fullScreenTextArea.setOnClickListener(v -> {
- fullScreenCheckBox.toggle();
- updateCapabilities(false);
- updateAlertDialogEnableState(fullScreenTextArea, windowTextArea);
- });
-
- windowTextArea.setOnClickListener(v -> {
- windowCheckBox.toggle();
- updateCapabilities(false);
- updateAlertDialogEnableState(fullScreenTextArea, windowTextArea);
-
- if (isTripleTapEnabled() && windowCheckBox.isChecked()) {
- showDialog(DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
- }
- });
- }
-
- private void updateAlertDialogCheckState() {
- updateCheckStatus(mMagnifyWindowCheckBox,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
- updateCheckStatus(mMagnifyFullScreenCheckBox,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
-
- }
-
- private void updateCheckStatus(CheckBox checkBox, int mode) {
- checkBox.setChecked((mode & mCapabilities) != 0);
- }
-
- private void updateAlertDialogEnableState(View fullScreenTextArea, View windowTextArea) {
- switch (mCapabilities) {
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN:
- setViewAndChildrenEnabled(fullScreenTextArea, false);
- break;
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW:
- setViewAndChildrenEnabled(windowTextArea, false);
- break;
- case Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL:
- setViewAndChildrenEnabled(fullScreenTextArea, true);
- setViewAndChildrenEnabled(windowTextArea, true);
- break;
- default:
- throw new IllegalArgumentException(
- "Unsupported ACCESSIBILITY_MAGNIFICATION_CAPABILITY " + mCapabilities);
- }
- }
-
- private void setViewAndChildrenEnabled(View view, boolean enabled) {
- view.setEnabled(enabled);
- if (view instanceof ViewGroup) {
- final ViewGroup viewGroup = (ViewGroup) view;
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- View child = viewGroup.getChildAt(i);
- setViewAndChildrenEnabled(child, enabled);
- }
- }
- }
-
- private void updateCapabilities(boolean saveToDB) {
- int capabilities = 0;
- capabilities |=
- mMagnifyFullScreenCheckBox.isChecked()
- ? Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN : 0;
- capabilities |= mMagnifyWindowCheckBox.isChecked()
- ? Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW : 0;
- mCapabilities = capabilities;
- if (saveToDB) {
- MagnificationCapabilities.setCapabilities(getPrefContext(), mCapabilities);
- }
- }
-
private boolean isTripleTapEnabled() {
return Settings.Secure.getInt(getPrefContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, OFF) == ON;
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index ec22a28..cf9c08b 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -608,19 +608,15 @@
final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
mComponentName.flattenToString(), UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List<CharSequence> list = new ArrayList<>();
- if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List<CharSequence> list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
index f65bd62..61459c4 100644
--- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
@@ -80,6 +80,8 @@
};
final View view = super.onCreateView(inflater, container, savedInstanceState);
+ // Parent sets the title when creating the view, so set it after calling super
+ mToggleServiceSwitchPreference.setTitle(R.string.reduce_bright_colors_switch_title);
updateGeneralCategoryOrder();
return view;
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index dde5be1..738d284 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -226,27 +226,23 @@
return context.getText(R.string.switch_off_text);
}
- final int shortcutType = PreferredShortcuts.retrieveUserShortcutType(context,
+ final int shortcutTypes = PreferredShortcuts.retrieveUserShortcutType(context,
MAGNIFICATION_CONTROLLER_NAME, UserShortcutType.SOFTWARE);
- int resId = R.string.accessibility_shortcut_edit_summary_software;
- if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
- resId = AccessibilityUtil.isTouchExploreEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
- : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
- }
- final CharSequence softwareTitle = context.getText(resId);
- List<CharSequence> list = new ArrayList<>();
- if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
+ final List<CharSequence> list = new ArrayList<>();
+ final CharSequence softwareTitle = context.getText(
+ R.string.accessibility_shortcut_edit_summary_software);
+
+ if (hasShortcutType(shortcutTypes, UserShortcutType.SOFTWARE)) {
list.add(softwareTitle);
}
- if ((shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.HARDWARE)) {
final CharSequence hardwareTitle = context.getText(
R.string.accessibility_shortcut_hardware_keyword);
list.add(hardwareTitle);
}
- if ((shortcutType & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
+ if (hasShortcutType(shortcutTypes, UserShortcutType.TRIPLETAP)) {
final CharSequence tripleTapTitle = context.getText(
R.string.accessibility_shortcut_triple_tap_keyword);
list.add(tripleTapTitle);
diff --git a/src/com/android/settings/applications/AppStateMediaManagementAppsBridge.java b/src/com/android/settings/applications/AppStateMediaManagementAppsBridge.java
new file mode 100644
index 0000000..ff2b4d8
--- /dev/null
+++ b/src/com/android/settings/applications/AppStateMediaManagementAppsBridge.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.content.Context;
+
+import com.android.settingslib.applications.ApplicationsState;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+import com.android.settingslib.applications.ApplicationsState.AppFilter;
+
+import java.util.List;
+
+/**
+ * Retrieves information from {@link AppOpsManager} and {@link android.content.pm.PackageManager}
+ * regarding {@link AppOpsManager#OP_MANAGE_MEDIA} and
+ * {@link Manifest.permission#MANAGE_MEDIA}.
+ */
+public class AppStateMediaManagementAppsBridge extends AppStateAppOpsBridge {
+
+ private final AppOpsManager mAppOpsManager;
+
+ public AppStateMediaManagementAppsBridge(Context context, ApplicationsState appState,
+ Callback callback) {
+ super(context, appState, callback,
+ AppOpsManager.strOpToOp(AppOpsManager.OPSTR_MANAGE_MEDIA),
+ new String[]{Manifest.permission.MANAGE_MEDIA});
+
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+ }
+
+ @Override
+ protected void updateExtraInfo(AppEntry app, String pkg, int uid) {
+ app.extraInfo = createPermissionState(pkg, uid);
+ }
+
+ @Override
+ protected void loadAllExtraInfo() {
+ super.loadAllExtraInfo();
+ final List<AppEntry> allApps = mAppSession.getAllApps();
+ final int appCount = allApps.size();
+ for (int i = 0; i < appCount; i++) {
+ final AppEntry appEntry = allApps.get(i);
+ if (appEntry.extraInfo instanceof PermissionState) {
+ updateExtraInfo(appEntry, appEntry.info.packageName, appEntry.info.uid);
+ }
+ }
+ }
+
+ /**
+ * Returns information regarding {@link Manifest.permission#MANAGE_MEDIA} for the given
+ * package and uid.
+ */
+ public PermissionState createPermissionState(String packageName, int uid) {
+ final PermissionState permissionState = getPermissionInfo(packageName, uid);
+ permissionState.appOpMode = mAppOpsManager.unsafeCheckOpNoThrow(
+ AppOpsManager.OPSTR_MANAGE_MEDIA, uid, packageName);
+ return permissionState;
+ }
+
+ /**
+ * Used by {@link com.android.settings.applications.manageapplications.AppFilterRegistry} to
+ * determine which apps get to appear on the Special App Access list.
+ */
+ public static final AppFilter FILTER_MEDIA_MANAGEMENT_APPS = new AppFilter() {
+ @Override
+ public void init() {
+ }
+
+ @Override
+ public boolean filterApp(AppEntry info) {
+ return info.extraInfo != null;
+ }
+ };
+}
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index 307ceb1..6d515a3 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.content.pm.PackageInfo;
-import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
@@ -31,14 +30,11 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
import com.android.settings.fuelgauge.BatteryEntry;
-import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -46,7 +42,6 @@
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
-import java.util.ArrayList;
import java.util.List;
public class AppBatteryPreferenceController extends BasePreferenceController
@@ -54,22 +49,11 @@
private static final String KEY_BATTERY = "battery";
- // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to
- // BatteryStatsHelper and BatterySipper
- @VisibleForTesting
- final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks =
- new BatteryStatsHelperLoaderCallbacks();
@VisibleForTesting
final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks =
new BatteryUsageStatsLoaderCallbacks();
-
- @VisibleForTesting
- BatterySipper mSipper;
- @VisibleForTesting
- BatteryStatsHelper mBatteryHelper;
@VisibleForTesting
BatteryUtils mBatteryUtils;
-
@VisibleForTesting
BatteryUsageStats mBatteryUsageStats;
@VisibleForTesting
@@ -113,11 +97,10 @@
if (isBatteryStatsAvailable()) {
final UserManager userManager =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
- final BatteryEntry entry = new BatteryEntry(mContext, null, userManager, mSipper,
- mUidBatteryConsumer);
- entry.defaultPackageName = mPackageName;
+ final BatteryEntry entry = new BatteryEntry(mContext, /* handler */null, userManager,
+ mUidBatteryConsumer, /* isHidden */ false, /* packages */ null, mPackageName);
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
- mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, mBatteryPercent);
+ entry, mBatteryPercent);
} else {
AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
mPackageName);
@@ -128,29 +111,23 @@
@Override
public void onResume() {
mParent.getLoaderManager().restartLoader(
- AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY,
- mBatteryStatsHelperLoaderCallbacks);
- mParent.getLoaderManager().restartLoader(
AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY,
mBatteryUsageStatsLoaderCallbacks);
}
@Override
public void onPause() {
- mParent.getLoaderManager().destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY);
mParent.getLoaderManager().destroyLoader(
AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS);
}
private void onLoadFinished() {
- // Wait for both loaders to finish before proceeding.
- if (mBatteryHelper == null || mBatteryUsageStats == null) {
+ if (mBatteryUsageStats == null) {
return;
}
final PackageInfo packageInfo = mParent.getPackageInfo();
if (packageInfo != null) {
- mSipper = findTargetSipper(mBatteryHelper, packageInfo.applicationInfo.uid);
mUidBatteryConsumer = findTargetUidBatteryConsumer(mBatteryUsageStats,
packageInfo.applicationInfo.uid);
if (mParent.getActivity() != null) {
@@ -163,13 +140,9 @@
void updateBattery() {
mPreference.setEnabled(true);
if (isBatteryStatsAvailable()) {
- final int dischargePercentage = mBatteryUsageStats.getDischargePercentage();
-
- final List<BatterySipper> usageList = new ArrayList<>(mBatteryHelper.getUsageList());
- final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList);
final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(),
- hiddenAmount, dischargePercentage);
+ mBatteryUsageStats.getDischargePercentage());
mBatteryPercent = Utils.formatPercentage(percentOfMax);
mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent));
} else {
@@ -179,19 +152,7 @@
@VisibleForTesting
boolean isBatteryStatsAvailable() {
- return mBatteryHelper != null && mSipper != null && mUidBatteryConsumer != null;
- }
-
- @VisibleForTesting
- BatterySipper findTargetSipper(BatteryStatsHelper batteryHelper, int uid) {
- final List<BatterySipper> usageList = batteryHelper.getUsageList();
- for (int i = 0, size = usageList.size(); i < size; i++) {
- final BatterySipper sipper = usageList.get(i);
- if (sipper.getUid() == uid) {
- return sipper;
- }
- }
- return null;
+ return mUidBatteryConsumer != null;
}
@VisibleForTesting
@@ -206,25 +167,6 @@
return null;
}
- private class BatteryStatsHelperLoaderCallbacks
- implements LoaderManager.LoaderCallbacks<BatteryStatsHelper> {
- @Override
- public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
- return new BatteryStatsHelperLoader(mContext);
- }
-
- @Override
- public void onLoadFinished(Loader<BatteryStatsHelper> loader,
- BatteryStatsHelper batteryHelper) {
- mBatteryHelper = batteryHelper;
- AppBatteryPreferenceController.this.onLoadFinished();
- }
-
- @Override
- public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
- }
- }
-
private class BatteryUsageStatsLoaderCallbacks
implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
@Override
diff --git a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
index 205b6d2..23dd960 100644
--- a/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppVersionPreferenceController.java
@@ -17,6 +17,7 @@
package com.android.settings.applications.appinfo;
import android.content.Context;
+import android.content.pm.PackageInfo;
import android.text.BidiFormatter;
import com.android.settings.R;
@@ -29,7 +30,13 @@
@Override
public CharSequence getSummary() {
+ // TODO(b/168333280): Review the null case in detail since this is just a quick
+ // workaround to fix NPE.
+ final PackageInfo packageInfo = mParent.getPackageInfo();
+ if (packageInfo == null) {
+ return null;
+ }
return mContext.getString(R.string.version_text,
- BidiFormatter.getInstance().unicodeWrap(mParent.getPackageInfo().versionName));
+ BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName));
}
}
diff --git a/src/com/android/settings/applications/appinfo/MediaManagementAppsDetails.java b/src/com/android/settings/applications/appinfo/MediaManagementAppsDetails.java
new file mode 100644
index 0000000..f60fb4f
--- /dev/null
+++ b/src/com/android/settings/applications/appinfo/MediaManagementAppsDetails.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.applications.appinfo;
+
+import android.app.AppOpsManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.applications.AppInfoWithHeader;
+import com.android.settings.applications.AppStateAppOpsBridge.PermissionState;
+import com.android.settings.applications.AppStateMediaManagementAppsBridge;
+import com.android.settingslib.applications.ApplicationsState.AppEntry;
+
+/**
+ * Class for displaying app info related to {@link AppOpsManager#OP_MANAGE_MEDIA}.
+ */
+public class MediaManagementAppsDetails extends AppInfoWithHeader implements
+ OnPreferenceChangeListener {
+
+ private static final String KEY_SWITCH_PREF = "media_management_apps_toggle";
+
+ private AppStateMediaManagementAppsBridge mAppBridge;
+ private AppOpsManager mAppOpsManager;
+ private SwitchPreference mSwitchPref;
+ private PermissionState mPermissionState;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Context context = getActivity();
+ mAppBridge = new AppStateMediaManagementAppsBridge(context, mState, null /* callback */);
+ mAppOpsManager = context.getSystemService(AppOpsManager.class);
+
+ // initialize preferences
+ addPreferencesFromResource(R.xml.media_management_apps);
+ mSwitchPref = findPreference(KEY_SWITCH_PREF);
+
+ // install event listeners
+ mSwitchPref.setOnPreferenceChangeListener(this);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean value = (Boolean) newValue;
+ if (preference == mSwitchPref) {
+ if (mPermissionState != null && value != mPermissionState.isPermissible()) {
+ setCanManageMedia(value);
+ logPermissionChange(value, mPackageName);
+ refreshUi();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void setCanManageMedia(boolean newState) {
+ mAppOpsManager.setUidMode(AppOpsManager.OP_MANAGE_MEDIA, mPackageInfo.applicationInfo.uid,
+ newState ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_ERRORED);
+ }
+
+ private void logPermissionChange(boolean newState, String packageName) {
+ mMetricsFeatureProvider.action(
+ mMetricsFeatureProvider.getAttribution(getActivity()),
+ SettingsEnums.ACTION_MEDIA_MANAGEMENT_APPS_TOGGLE,
+ getMetricsCategory(),
+ packageName,
+ newState ? 1 : 0);
+ }
+
+ @Override
+ protected boolean refreshUi() {
+ if (mPackageInfo == null || mPackageInfo.applicationInfo == null) {
+ return false;
+ }
+
+ mPermissionState = mAppBridge.createPermissionState(mPackageName,
+ mPackageInfo.applicationInfo.uid);
+ mSwitchPref.setEnabled(mPermissionState.permissionDeclared);
+ mSwitchPref.setChecked(mPermissionState.isPermissible());
+ return true;
+ }
+
+ @Override
+ protected AlertDialog createDialog(int id, int errorCode) {
+ return null;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.MEDIA_MANAGEMENT_APPS;
+ }
+
+ /**
+ * Returns the string that states whether the app has access to
+ * {@link android.Manifest.permission#MANAGE_MEDIA}.
+ */
+ public static int getSummary(Context context, AppEntry entry) {
+ final PermissionState state;
+ if (entry.extraInfo instanceof PermissionState) {
+ state = (PermissionState) entry.extraInfo;
+ } else {
+ state = new AppStateMediaManagementAppsBridge(context, null /* appState */,
+ null /* callback */).createPermissionState(entry.info.packageName,
+ entry.info.uid);
+ }
+
+ return state.isPermissible() ? R.string.app_permission_summary_allowed
+ : R.string.app_permission_summary_not_allowed;
+ }
+}
diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
index 73d80a3..8e8e072 100644
--- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
+++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java
@@ -16,7 +16,7 @@
package com.android.settings.applications.defaultapps;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import android.content.Context;
import android.content.Intent;
@@ -33,9 +33,9 @@
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.GearPreference;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.widget.TwoTargetPreference;
public abstract class DefaultAppPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
diff --git a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
index f4de226..d1d4f62 100644
--- a/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
+++ b/src/com/android/settings/applications/manageapplications/AppFilterRegistry.java
@@ -22,6 +22,7 @@
import com.android.settings.applications.AppStateAlarmsAndRemindersBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateManageExternalStorageBridge;
+import com.android.settings.applications.AppStateMediaManagementAppsBridge;
import com.android.settings.applications.AppStateNotificationBridge;
import com.android.settings.applications.AppStateOverlayBridge;
import com.android.settings.applications.AppStatePowerBridge;
@@ -52,6 +53,7 @@
FILTER_APPS_INSTALL_SOURCES,
FILTER_APPS_BLOCKED,
FILTER_ALARMS_AND_REMINDERS,
+ FILTER_APPS_MEDIA_MANAGEMENT,
})
@interface FilterType {
}
@@ -76,14 +78,15 @@
public static final int FILTER_APPS_BLOCKED = 16;
public static final int FILTER_MANAGE_EXTERNAL_STORAGE = 17;
public static final int FILTER_ALARMS_AND_REMINDERS = 18;
- // Next id: 18. If you add an entry here, length of mFilters should be updated
+ public static final int FILTER_APPS_MEDIA_MANAGEMENT = 19;
+ // Next id: 20. If you add an entry here, length of mFilters should be updated
private static AppFilterRegistry sRegistry;
private final AppFilterItem[] mFilters;
private AppFilterRegistry() {
- mFilters = new AppFilterItem[19];
+ mFilters = new AppFilterItem[20];
// High power allowlist, on
mFilters[FILTER_APPS_POWER_ALLOWLIST] = new AppFilterItem(
@@ -194,6 +197,12 @@
AppStateAlarmsAndRemindersBridge.FILTER_CLOCK_APPS,
FILTER_ALARMS_AND_REMINDERS,
R.string.alarms_and_reminders_title);
+
+ // Apps that can manage media files
+ mFilters[FILTER_APPS_MEDIA_MANAGEMENT] = new AppFilterItem(
+ AppStateMediaManagementAppsBridge.FILTER_MEDIA_MANAGEMENT_APPS,
+ FILTER_APPS_MEDIA_MANAGEMENT,
+ R.string.media_management_apps_title);
}
public static AppFilterRegistry getInstance() {
@@ -224,6 +233,8 @@
return FILTER_MANAGE_EXTERNAL_STORAGE;
case ManageApplications.LIST_TYPE_ALARMS_AND_REMINDERS:
return FILTER_ALARMS_AND_REMINDERS;
+ case ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS:
+ return FILTER_APPS_MEDIA_MANAGEMENT;
default:
return FILTER_APPS_ALL;
}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 2dadbbc..e98555b 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -96,6 +96,7 @@
import com.android.settings.applications.AppStateBaseBridge;
import com.android.settings.applications.AppStateInstallAppsBridge;
import com.android.settings.applications.AppStateManageExternalStorageBridge;
+import com.android.settings.applications.AppStateMediaManagementAppsBridge;
import com.android.settings.applications.AppStateNotificationBridge;
import com.android.settings.applications.AppStateNotificationBridge.NotificationsSentState;
import com.android.settings.applications.AppStateOverlayBridge;
@@ -110,6 +111,7 @@
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.ManageExternalStorageDetails;
+import com.android.settings.applications.appinfo.MediaManagementAppsDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.InstrumentedFragment;
@@ -233,6 +235,7 @@
public static final int LIST_TYPE_WIFI_ACCESS = 13;
public static final int LIST_MANAGE_EXTERNAL_STORAGE = 14;
public static final int LIST_TYPE_ALARMS_AND_REMINDERS = 15;
+ public static final int LIST_TYPE_MEDIA_MANAGEMENT_APPS = 16;
// List types that should show instant apps.
public static final Set<Integer> LIST_TYPES_WITH_INSTANT = new ArraySet<>(Arrays.asList(
@@ -324,6 +327,9 @@
} else if (className.equals(Settings.ManageExternalStorageActivity.class.getName())) {
mListType = LIST_MANAGE_EXTERNAL_STORAGE;
screenTitle = R.string.manage_external_storage_title;
+ } else if (className.equals(Settings.MediaManagementAppsActivity.class.getName())) {
+ mListType = LIST_TYPE_MEDIA_MANAGEMENT_APPS;
+ screenTitle = R.string.media_management_apps_title;
} else if (className.equals(Settings.AlarmsAndRemindersActivity.class.getName())) {
mListType = LIST_TYPE_ALARMS_AND_REMINDERS;
screenTitle = R.string.alarms_and_reminders_title;
@@ -553,6 +559,8 @@
return SettingsEnums.MANAGE_EXTERNAL_STORAGE;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return SettingsEnums.ALARMS_AND_REMINDERS;
+ case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
+ return SettingsEnums.MEDIA_MANAGEMENT_APPS;
default:
return SettingsEnums.PAGE_UNKNOWN;
}
@@ -678,6 +686,10 @@
startAppInfoFragment(AlarmsAndRemindersDetails.class,
R.string.alarms_and_reminders_label);
break;
+ case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
+ startAppInfoFragment(MediaManagementAppsDetails.class,
+ R.string.media_management_apps_title);
+ break;
// TODO: Figure out if there is a way where we can spin up the profile's settings
// process ahead of time, to avoid a long load of data when user clicks on a managed
// app. Maybe when they load the list of apps that contains managed profile apps.
@@ -758,6 +770,8 @@
return R.string.help_uri_manage_external_storage;
case LIST_TYPE_ALARMS_AND_REMINDERS:
return R.string.help_uri_alarms_and_reminders;
+ case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
+ return R.string.help_uri_media_management_apps;
default:
case LIST_TYPE_MAIN:
return R.string.help_uri_apps;
@@ -1082,6 +1096,8 @@
mExtraInfoBridge = new AppStateManageExternalStorageBridge(mContext, mState, this);
} else if (mManageApplications.mListType == LIST_TYPE_ALARMS_AND_REMINDERS) {
mExtraInfoBridge = new AppStateAlarmsAndRemindersBridge(mContext, mState, this);
+ } else if (mManageApplications.mListType == LIST_TYPE_MEDIA_MANAGEMENT_APPS) {
+ mExtraInfoBridge = new AppStateMediaManagementAppsBridge(mContext, mState, this);
} else {
mExtraInfoBridge = null;
}
@@ -1546,6 +1562,9 @@
case LIST_TYPE_ALARMS_AND_REMINDERS:
holder.setSummary(AlarmsAndRemindersDetails.getSummary(mContext, entry));
break;
+ case LIST_TYPE_MEDIA_MANAGEMENT_APPS:
+ holder.setSummary(MediaManagementAppsDetails.getSummary(mContext, entry));
+ break;
default:
holder.updateSizeText(entry, mManageApplications.mInvalidSizeStr, mWhichSize);
break;
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
index 7c8b96f..1fe3f17 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
@@ -16,6 +16,8 @@
package com.android.settings.applications.specialaccess.deviceadmin;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -661,7 +663,11 @@
mAdminWarning.setText(R.string.admin_profile_owner_user_message);
} else {
// Show device owner description.
- mAdminWarning.setText(R.string.admin_device_owner_message);
+ if (isFinancedDevice()) {
+ mAdminWarning.setText(R.string.admin_financed_message);
+ } else {
+ mAdminWarning.setText(R.string.admin_device_owner_message);
+ }
}
mActionButton.setText(R.string.remove_device_admin);
mActionButton.setEnabled(false);
@@ -759,6 +765,11 @@
return info != null ? info.isManagedProfile() : false;
}
+ private boolean isFinancedDevice() {
+ return mDPM.isDeviceManaged() && mDPM.getDeviceOwnerType(
+ mDPM.getDeviceOwnerComponentOnAnyUser()) == DEVICE_OWNER_TYPE_FINANCED;
+ }
+
/**
* @return an {@link Optional} containing the admin with a given package name, if it exists,
* or {@link Optional#empty()} otherwise.
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 3e0591f..5e6f7e9 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -63,8 +63,8 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.TwoTargetPreference;
import java.util.HashMap;
import java.util.List;
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index 95c94ce..c90fe48 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -30,7 +30,6 @@
import android.util.Log;
import androidx.annotation.Nullable;
-import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -39,7 +38,6 @@
import com.android.settings.slices.SliceData;
import com.android.settings.slices.Sliceable;
import com.android.settingslib.core.AbstractPreferenceController;
-import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexableRaw;
import java.lang.annotation.Retention;
@@ -52,9 +50,6 @@
* Abstract class to consolidate utility between preference controllers and act as an interface
* for Slices. The abstract classes that inherit from this class will act as the direct interfaces
* for each type when plugging into Slices.
- * <p>
- * Controllers defined in xml are automatically {@link Lifecycle#addObserver(LifecycleObserver)
- * wired up} to the settings lifecycle if they implement {@link LifecycleObserver}.
*/
public abstract class BasePreferenceController extends AbstractPreferenceController implements
Sliceable {
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 5e51284..c08c149 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -45,6 +45,7 @@
import com.android.settings.applications.appinfo.DrawOverlayDetails;
import com.android.settings.applications.appinfo.ExternalSourcesDetails;
import com.android.settings.applications.appinfo.ManageExternalStorageDetails;
+import com.android.settings.applications.appinfo.MediaManagementAppsDetails;
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.applications.appops.BackgroundCheckSummary;
import com.android.settings.applications.assist.ManageAssist;
@@ -318,6 +319,7 @@
MediaControlsSettings.class.getName(),
NetworkProviderSettings.class.getName(),
AlarmsAndRemindersDetails.class.getName(),
+ MediaManagementAppsDetails.class.getName()
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 7acbd19..1b2be80 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -27,7 +27,6 @@
import androidx.annotation.CallSuper;
import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
@@ -45,6 +44,7 @@
import com.android.settings.widget.PrimarySwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.ProviderTile;
diff --git a/src/com/android/settings/datausage/AppDataUsageActivity.java b/src/com/android/settings/datausage/AppDataUsageActivity.java
index 82a3a45..48bedce 100644
--- a/src/com/android/settings/datausage/AppDataUsageActivity.java
+++ b/src/com/android/settings/datausage/AppDataUsageActivity.java
@@ -17,7 +17,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
-import android.provider.Settings;
import android.util.Log;
import com.android.settings.R;
diff --git a/src/com/android/settings/datausage/DataUsageUtils.java b/src/com/android/settings/datausage/DataUsageUtils.java
index da0ca63..7da69cb 100644
--- a/src/com/android/settings/datausage/DataUsageUtils.java
+++ b/src/com/android/settings/datausage/DataUsageUtils.java
@@ -14,13 +14,14 @@
package com.android.settings.datausage;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_ETHERNET;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.telephony.TelephonyManager.SIM_STATE_READY;
import android.app.usage.NetworkStats.Bucket;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkTemplate;
import android.os.RemoteException;
@@ -69,8 +70,7 @@
return SystemProperties.get(DataUsageUtils.TEST_RADIOS_PROP).contains(ETHERNET);
}
- final ConnectivityManager conn = context.getSystemService(ConnectivityManager.class);
- if (!conn.isNetworkSupported(ConnectivityManager.TYPE_ETHERNET)) {
+ if (!context.getPackageManager().hasSystemFeature(FEATURE_ETHERNET)) {
return false;
}
@@ -96,10 +96,8 @@
* TODO: This is the opposite to Utils.isWifiOnly(), it should be refactored into 1 method.
*/
public static boolean hasMobileData(Context context) {
- final ConnectivityManager connectivityManager =
- context.getSystemService(ConnectivityManager.class);
- return connectivityManager != null && connectivityManager
- .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ final TelephonyManager tele = context.getSystemService(TelephonyManager.class);
+ return tele.isDataCapable();
}
/**
@@ -128,12 +126,13 @@
Log.d(TAG, "hasReadyMobileRadio: subInfo=" + subInfo);
}
}
- final ConnectivityManager conn = context.getSystemService(ConnectivityManager.class);
- final boolean retVal = conn.isNetworkSupported(TYPE_MOBILE) && isReady;
+
+ final boolean isDataCapable = tele.isDataCapable();
+ final boolean retVal = isDataCapable && isReady;
if (LOGD) {
Log.d(TAG, "hasReadyMobileRadio:"
- + " conn.isNetworkSupported(TYPE_MOBILE)="
- + conn.isNetworkSupported(TYPE_MOBILE)
+ + " telephonManager.isDataCapable()="
+ + isDataCapable
+ " isReady=" + isReady);
}
return retVal;
@@ -147,9 +146,8 @@
return SystemProperties.get(TEST_RADIOS_PROP).contains("wifi");
}
- final ConnectivityManager connectivityManager =
- context.getSystemService(ConnectivityManager.class);
- return connectivityManager != null && connectivityManager.isNetworkSupported(TYPE_WIFI);
+ final PackageManager packageManager = context.getPackageManager();
+ return packageManager != null && packageManager.hasSystemFeature(FEATURE_WIFI);
}
/**
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
index 00f4a7b..e3c79a7 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreferenceController.java
@@ -42,7 +42,6 @@
import java.util.Set;
import java.util.TreeSet;
-
public class UnrestrictedDataAccessPreferenceController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop, OnDestroy, ApplicationsState.Callbacks,
AppStateBaseBridge.Callback, Preference.OnPreferenceChangeListener {
diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
index 1a45640..0b0fa27 100644
--- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
@@ -15,10 +15,10 @@
*/
package com.android.settings.datetime;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import android.app.time.TimeManager;
import android.app.time.TimeZoneCapabilities;
diff --git a/src/com/android/settings/development/AdbIpAddressPreferenceController.java b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
index 45fe51b..ce5a851 100644
--- a/src/com/android/settings/development/AdbIpAddressPreferenceController.java
+++ b/src/com/android/settings/development/AdbIpAddressPreferenceController.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.debug.IAdbManager;
import android.net.ConnectivityManager;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.wifi.WifiManager;
import android.os.RemoteException;
@@ -135,7 +136,7 @@
return null;
}
- Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+ Iterator<LinkAddress> iter = prop.getAllLinkAddresses().iterator();
// If there are no entries, return null
if (!iter.hasNext()) {
return null;
@@ -144,7 +145,7 @@
// Concatenate all available addresses, newline separated
StringBuilder addresses = new StringBuilder();
while (iter.hasNext()) {
- InetAddress addr = iter.next();
+ InetAddress addr = iter.next().getAddress();
if (addr instanceof Inet4Address) {
// adb only supports ipv4 at the moment
addresses.append(addr.getHostAddress());
diff --git a/src/com/android/settings/deviceinfo/MigrateEstimateTask.java b/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
index a5790b3..9198ade 100644
--- a/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
+++ b/src/com/android/settings/deviceinfo/MigrateEstimateTask.java
@@ -29,9 +29,9 @@
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
-import android.util.Log;
import android.text.format.DateUtils;
import android.text.format.Formatter;
+import android.util.Log;
import java.io.IOException;
import java.util.UUID;
diff --git a/src/com/android/settings/deviceinfo/PercentageBarChart.java b/src/com/android/settings/deviceinfo/PercentageBarChart.java
index cb25b81..e69de29 100644
--- a/src/com/android/settings/deviceinfo/PercentageBarChart.java
+++ b/src/com/android/settings/deviceinfo/PercentageBarChart.java
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2010 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.deviceinfo;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.settings.R;
-
-import java.util.Collection;
-
-/**
- * Draws a horizontal bar chart with colored slices, each represented by
- * {@link Entry}.
- */
-public class PercentageBarChart extends View {
- private final Paint mEmptyPaint = new Paint();
-
- private Collection<Entry> mEntries;
-
- private int mMinTickWidth = 1;
-
- public static class Entry implements Comparable<Entry> {
- public final int order;
- public final float percentage;
- public final Paint paint;
-
- protected Entry(int order, float percentage, Paint paint) {
- this.order = order;
- this.percentage = percentage;
- this.paint = paint;
- }
-
- @Override
- public int compareTo(Entry another) {
- return order - another.order;
- }
- }
-
- public PercentageBarChart(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PercentageBarChart);
- mMinTickWidth = a.getDimensionPixelSize(R.styleable.PercentageBarChart_minTickWidth, 1);
- int emptyColor = a.getColor(R.styleable.PercentageBarChart_emptyColor, Color.BLACK);
- a.recycle();
-
- mEmptyPaint.setColor(emptyColor);
- mEmptyPaint.setStyle(Paint.Style.FILL);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- final int left = getPaddingLeft();
- final int right = getWidth() - getPaddingRight();
- final int top = getPaddingTop();
- final int bottom = getHeight() - getPaddingBottom();
-
- final int width = right - left;
-
- final boolean isLayoutRtl = isLayoutRtl();
- if (isLayoutRtl) {
- float nextX = right;
-
- if (mEntries != null) {
- for (final Entry e : mEntries) {
- final float entryWidth;
- if (e.percentage == 0.0f) {
- entryWidth = 0.0f;
- } else {
- entryWidth = Math.max(mMinTickWidth, width * e.percentage);
- }
-
- final float lastX = nextX - entryWidth;
- if (lastX < left) {
- canvas.drawRect(left, top, nextX, bottom, e.paint);
- return;
- }
-
- canvas.drawRect(lastX, top, nextX, bottom, e.paint);
- nextX = lastX;
- }
- }
-
- canvas.drawRect(left, top, nextX, bottom, mEmptyPaint);
- } else {
- float lastX = left;
-
- if (mEntries != null) {
- for (final Entry e : mEntries) {
- final float entryWidth;
- if (e.percentage == 0.0f) {
- entryWidth = 0.0f;
- } else {
- entryWidth = Math.max(mMinTickWidth, width * e.percentage);
- }
-
- final float nextX = lastX + entryWidth;
- if (nextX > right) {
- canvas.drawRect(lastX, top, right, bottom, e.paint);
- return;
- }
-
- canvas.drawRect(lastX, top, nextX, bottom, e.paint);
- lastX = nextX;
- }
- }
-
- canvas.drawRect(lastX, top, right, bottom, mEmptyPaint);
- }
- }
-
- /**
- * Sets the background for this chart. Callers are responsible for later
- * calling {@link #invalidate()}.
- */
- @Override
- public void setBackgroundColor(int color) {
- mEmptyPaint.setColor(color);
- }
-
- /**
- * Adds a new slice to the percentage bar chart. Callers are responsible for
- * later calling {@link #invalidate()}.
- *
- * @param percentage the total width that
- * @param color the color to draw the entry
- */
- public static Entry createEntry(int order, float percentage, int color) {
- final Paint p = new Paint();
- p.setColor(color);
- p.setStyle(Paint.Style.FILL);
- return new Entry(order, percentage, p);
- }
-
- public void setEntries(Collection<Entry> entries) {
- mEntries = entries;
- }
-}
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java b/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java
deleted file mode 100644
index 00a79a0..0000000
--- a/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.deviceinfo;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.storage.VolumeInfo;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.settings.R;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
-import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
-import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
-
-import java.util.Objects;
-
-/**
- * Handles the option menu on the Storage settings.
- */
-public class PrivateVolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
- OnPrepareOptionsMenu, OnOptionsItemSelected {
- private static final int OPTIONS_MENU_MIGRATE_DATA = 100;
-
- private Context mContext;
- private VolumeInfo mVolumeInfo;
- private PackageManager mPm;
-
- public PrivateVolumeOptionMenuController(
- Context context, VolumeInfo volumeInfo, PackageManager packageManager) {
- mContext = context;
- mVolumeInfo = volumeInfo;
- mPm = packageManager;
- }
-
- @Override
- public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
- menu.add(Menu.NONE, OPTIONS_MENU_MIGRATE_DATA, 0, R.string.storage_menu_migrate);
- }
-
- @Override
- public void onPrepareOptionsMenu(Menu menu) {
- if (mVolumeInfo == null) {
- return;
- }
-
- // Only offer to migrate when not current storage
- final VolumeInfo privateVol = mPm.getPrimaryStorageCurrentVolume();
- final MenuItem migrate = menu.findItem(OPTIONS_MENU_MIGRATE_DATA);
- if (migrate != null) {
- migrate.setVisible((privateVol != null)
- && (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
- && !Objects.equals(mVolumeInfo, privateVol)
- && privateVol.isMountedWritable());
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- if (menuItem.getItemId() == OPTIONS_MENU_MIGRATE_DATA) {
- final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
- intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolumeInfo.getId());
- mContext.startActivity(intent);
- return true;
- }
- return false;
- }
-}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 10c3a43..7af4f0c 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -20,13 +20,18 @@
import android.app.settings.SettingsEnums;
import android.app.usage.StorageStatsManager;
import android.content.Context;
+import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
import android.provider.SearchIndexableResource;
+import android.text.TextUtils;
import android.util.SparseArray;
import android.view.View;
@@ -41,15 +46,22 @@
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
+import com.android.settings.deviceinfo.storage.DiskInitFragment;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
+import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageUtils;
import com.android.settings.deviceinfo.storage.UserIconLoader;
import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -57,48 +69,227 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
@SearchIndexable
public class StorageDashboardFragment extends DashboardFragment
implements
- LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
+ LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>>,
+ Preference.OnPreferenceClickListener {
private static final String TAG = "StorageDashboardFrag";
private static final String SUMMARY_PREF_KEY = "storage_summary";
+ private static final String FREE_UP_SPACE_PREF_KEY = "free_up_space";
+ private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
private static final int STORAGE_JOB_ID = 0;
private static final int ICON_JOB_ID = 1;
private static final int VOLUME_SIZE_JOB_ID = 2;
- private VolumeInfo mVolume;
+ private StorageManager mStorageManager;
+ private final List<StorageEntry> mStorageEntries = new ArrayList<>();
+ private StorageEntry mSelectedStorageEntry;
private PrivateStorageInfo mStorageInfo;
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
private CachedStorageValuesHelper mCachedStorageValuesHelper;
private StorageItemPreferenceController mPreferenceController;
- private PrivateVolumeOptionMenuController mOptionMenuController;
+ private VolumeOptionMenuController mOptionMenuController;
+ private StorageSelectionPreferenceController mStorageSelectionController;
+ private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
private List<AbstractPreferenceController> mSecondaryUsers;
private boolean mPersonalOnly;
+ private Preference mFreeUpSpacePreference;
+
+ private final StorageEventListener mStorageEventListener = new StorageEventListener() {
+ @Override
+ public void onVolumeStateChanged(VolumeInfo volumeInfo, int oldState, int newState) {
+ if (!isInteresting(volumeInfo)) {
+ return;
+ }
+
+ final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
+ switch (volumeInfo.getState()) {
+ case VolumeInfo.STATE_MOUNTED:
+ case VolumeInfo.STATE_MOUNTED_READ_ONLY:
+ case VolumeInfo.STATE_UNMOUNTABLE:
+ // Add mounted or unmountable storage in the list and show it on spinner.
+ // Unmountable storages are the storages which has a problem format and android
+ // is not able to mount it automatically.
+ // Users can format an unmountable storage by the UI and then use the storage.
+ mStorageEntries.removeIf(storageEntry -> {
+ return storageEntry.equals(changedStorageEntry);
+ });
+ mStorageEntries.add(changedStorageEntry);
+ if (changedStorageEntry.equals(mSelectedStorageEntry)) {
+ mSelectedStorageEntry = changedStorageEntry;
+ }
+ refreshUi();
+ break;
+ case VolumeInfo.STATE_REMOVED:
+ case VolumeInfo.STATE_UNMOUNTED:
+ case VolumeInfo.STATE_BAD_REMOVAL:
+ case VolumeInfo.STATE_EJECTING:
+ // Remove removed storage from list and don't show it on spinner.
+ if (mStorageEntries.remove(changedStorageEntry)) {
+ if (changedStorageEntry.equals(mSelectedStorageEntry)) {
+ mSelectedStorageEntry =
+ StorageEntry.getDefaultInternalStorageEntry(getContext());
+ }
+ refreshUi();
+ }
+ break;
+ default:
+ // Do nothing.
+ }
+ }
+
+ @Override
+ public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
+ if (isVolumeRecordMissed(volumeRecord)) {
+ // VolumeRecord is a metadata of VolumeInfo, if a VolumeInfo is missing
+ // (e.g., internal SD card is removed.) show the missing storage to users,
+ // users can insert the SD card or manually forget the storage from the device.
+ final StorageEntry storageEntry = new StorageEntry(volumeRecord);
+ if (!mStorageEntries.contains(storageEntry)) {
+ mStorageEntries.add(storageEntry);
+ refreshUi();
+ }
+ } else {
+ // Find mapped VolumeInfo and replace with existing one for something changed.
+ // (e.g., Renamed.)
+ final VolumeInfo mappedVolumeInfo =
+ mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid());
+ if (mappedVolumeInfo == null) {
+ return;
+ }
+
+ final boolean removeMappedStorageEntry = mStorageEntries.removeIf(storageEntry ->
+ storageEntry.isVolumeInfo()
+ && TextUtils.equals(storageEntry.getFsUuid(), volumeRecord.getFsUuid())
+ );
+ if (removeMappedStorageEntry) {
+ mStorageEntries.add(new StorageEntry(getContext(), mappedVolumeInfo));
+ refreshUi();
+ }
+ }
+ }
+
+ @Override
+ public void onVolumeForgotten(String fsUuid) {
+ final StorageEntry storageEntry = new StorageEntry(
+ new VolumeRecord(VolumeInfo.TYPE_PUBLIC, fsUuid));
+ if (mStorageEntries.remove(storageEntry)) {
+ if (mSelectedStorageEntry.equals(storageEntry)) {
+ mSelectedStorageEntry =
+ StorageEntry.getDefaultInternalStorageEntry(getContext());
+ }
+ refreshUi();
+ }
+ }
+
+ @Override
+ public void onDiskScanned(DiskInfo disk, int volumeCount) {
+ if (!isDiskUnsupported(disk)) {
+ return;
+ }
+ final StorageEntry storageEntry = new StorageEntry(disk);
+ if (!mStorageEntries.contains(storageEntry)) {
+ mStorageEntries.add(storageEntry);
+ refreshUi();
+ }
+ }
+
+ @Override
+ public void onDiskDestroyed(DiskInfo disk) {
+ final StorageEntry storageEntry = new StorageEntry(disk);
+ if (mStorageEntries.remove(storageEntry)) {
+ if (mSelectedStorageEntry.equals(storageEntry)) {
+ mSelectedStorageEntry =
+ StorageEntry.getDefaultInternalStorageEntry(getContext());
+ }
+ refreshUi();
+ }
+ }
+ };
+
+ private static boolean isInteresting(VolumeInfo volumeInfo) {
+ switch (volumeInfo.getType()) {
+ case VolumeInfo.TYPE_PRIVATE:
+ case VolumeInfo.TYPE_PUBLIC:
+ case VolumeInfo.TYPE_STUB:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * VolumeRecord is a metadata of VolumeInfo, this is the case where a VolumeInfo is missing.
+ * (e.g., internal SD card is removed.)
+ */
+ private boolean isVolumeRecordMissed(VolumeRecord volumeRecord) {
+ return volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE
+ && mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null;
+ }
+
+ /**
+ * A unsupported disk is the disk of problem format, android is not able to mount automatically.
+ */
+ private static boolean isDiskUnsupported(DiskInfo disk) {
+ return disk.volumeCount == 0 && disk.size > 0;
+ }
+
+ private void refreshUi() {
+ mStorageSelectionController.setStorageEntries(mStorageEntries);
+ mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
+ mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
+
+ mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
+ getActivity().invalidateOptionsMenu();
+
+ mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
+
+ if (mSelectedStorageEntry.isMounted()) {
+ getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
+ getLoaderManager()
+ .restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
+ getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
+ } else {
+ mPreferenceController.clearStorageSizeDisplay();
+ }
+ }
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- // Initialize the storage sizes that we can quickly calc.
final Activity activity = getActivity();
- StorageManager sm = activity.getSystemService(StorageManager.class);
- mVolume = Utils.maybeInitializeVolume(sm, getArguments());
+ mStorageManager = activity.getSystemService(StorageManager.class);
mPersonalOnly = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.PERSONAL;
- if (mVolume == null) {
- activity.finish();
- return;
+
+ if (icicle == null) {
+ final VolumeInfo specifiedVolumeInfo =
+ Utils.maybeInitializeVolume(mStorageManager, getArguments());
+ mSelectedStorageEntry = specifiedVolumeInfo == null
+ ? StorageEntry.getDefaultInternalStorageEntry(getContext())
+ : new StorageEntry(getContext(), specifiedVolumeInfo);
+ } else {
+ mSelectedStorageEntry = icicle.getParcelable(SELECTED_STORAGE_ENTRY_KEY);
}
+
+ initializePreference();
initializeOptionsMenu(activity);
+ }
+
+ private void initializePreference() {
if (mPersonalOnly) {
final Preference summary = getPreferenceScreen().findPreference(SUMMARY_PREF_KEY);
if (summary != null) {
summary.setVisible(false);
}
}
+ mFreeUpSpacePreference = getPreferenceScreen().findPreference(FREE_UP_SPACE_PREF_KEY);
+ mFreeUpSpacePreference.setOnPreferenceClickListener(this);
}
@Override
@@ -106,12 +297,25 @@
super.onAttach(context);
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
getFragmentManager());
+ mStorageSelectionController = use(StorageSelectionPreferenceController.class);
+ mStorageSelectionController.setOnItemSelectedListener(storageEntry -> {
+ mSelectedStorageEntry = storageEntry;
+ refreshUi();
+
+ if (storageEntry.isDiskInfoUnsupported() || storageEntry.isUnmountable()) {
+ DiskInitFragment.show(this, R.string.storage_dialog_unmountable,
+ storageEntry.getDiskId());
+ } else if (storageEntry.isVolumeRecordMissed()) {
+ StorageUtils.launchForgetMissingVolumeRecordFragment(getContext(), storageEntry);
+ }
+ });
+ mStorageUsageProgressBarController = use(StorageUsageProgressBarPreferenceController.class);
}
@VisibleForTesting
void initializeOptionsMenu(Activity activity) {
- mOptionMenuController = new PrivateVolumeOptionMenuController(
- activity, mVolume, activity.getPackageManager());
+ mOptionMenuController = new VolumeOptionMenuController(activity, this,
+ mSelectedStorageEntry);
getSettingsLifecycle().addObserver(mOptionMenuController);
setHasOptionsMenu(true);
activity.invalidateOptionsMenu();
@@ -133,10 +337,34 @@
@Override
public void onResume() {
super.onResume();
- getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
- getLoaderManager()
- .restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
- getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
+
+ mStorageEntries.clear();
+ mStorageEntries.addAll(mStorageManager.getVolumes().stream()
+ .filter(volumeInfo -> isInteresting(volumeInfo))
+ .map(volumeInfo -> new StorageEntry(getContext(), volumeInfo))
+ .collect(Collectors.toList()));
+ mStorageEntries.addAll(mStorageManager.getDisks().stream()
+ .filter(disk -> isDiskUnsupported(disk))
+ .map(disk -> new StorageEntry(disk))
+ .collect(Collectors.toList()));
+ mStorageEntries.addAll(mStorageManager.getVolumeRecords().stream()
+ .filter(volumeRecord -> isVolumeRecordMissed(volumeRecord))
+ .map(volumeRecord -> new StorageEntry(volumeRecord))
+ .collect(Collectors.toList()));
+ refreshUi();
+ mStorageManager.registerListener(mStorageEventListener);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mStorageManager.unregisterListener(mStorageEventListener);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putParcelable(SELECTED_STORAGE_ENTRY_KEY, mSelectedStorageEntry);
+ super.onSaveInstanceState(outState);
}
@Override
@@ -148,7 +376,7 @@
boolean stopLoading = false;
if (mStorageInfo != null) {
long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
- mPreferenceController.setVolume(mVolume);
+ mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
@@ -197,7 +425,7 @@
StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
- mVolume, new StorageManagerVolumeProvider(sm));
+ null /* volume */, new StorageManagerVolumeProvider(sm));
controllers.add(mPreferenceController);
final UserManager userManager = context.getSystemService(UserManager.class);
@@ -209,7 +437,7 @@
@VisibleForTesting
protected void setVolume(VolumeInfo info) {
- mVolume = info;
+ mSelectedStorageEntry = new StorageEntry(getContext(), info);
}
/**
@@ -260,7 +488,7 @@
Bundle args) {
final Context context = getContext();
return new StorageAsyncLoader(context, context.getSystemService(UserManager.class),
- mVolume.fsUuid,
+ mSelectedStorageEntry.getFsUuid(),
new StorageStatsSource(context),
context.getPackageManager());
}
@@ -277,6 +505,21 @@
public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
}
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (preference == mFreeUpSpacePreference) {
+ final Context context = getContext();
+ final MetricsFeatureProvider metricsFeatureProvider =
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ metricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
+ metricsFeatureProvider.action(context, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
+ final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+ context.startActivity(intent);
+ return true;
+ }
+ return false;
+ }
+
@VisibleForTesting
public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
mCachedStorageValuesHelper = helper;
@@ -340,8 +583,9 @@
}
private boolean isQuotaSupported() {
- final StorageStatsManager stats = getActivity().getSystemService(StorageStatsManager.class);
- return stats.isQuotaSupported(mVolume.fsUuid);
+ return mSelectedStorageEntry.isMounted()
+ && getActivity().getSystemService(StorageStatsManager.class)
+ .isQuotaSupported(mSelectedStorageEntry.getFsUuid());
}
/**
@@ -378,11 +622,12 @@
implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
@Override
public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
- Context context = getContext();
- StorageManager sm = context.getSystemService(StorageManager.class);
- StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(sm);
+ final Context context = getContext();
+ final StorageManagerVolumeProvider smvp =
+ new StorageManagerVolumeProvider(mStorageManager);
final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
- return new VolumeSizesLoader(context, smvp, stats, mVolume);
+ return new VolumeSizesLoader(context, smvp, stats,
+ mSelectedStorageEntry.getVolumeInfo());
}
@Override
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
index 6294ab9..bd7ca82 100644
--- a/src/com/android/settings/deviceinfo/StorageVolumePreference.java
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
@@ -19,7 +19,6 @@
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
diff --git a/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
new file mode 100644
index 0000000..0932447
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo;
+
+import android.app.ActivityManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+import com.android.settings.deviceinfo.storage.StorageEntry;
+import com.android.settings.deviceinfo.storage.StorageRenameFragment;
+import com.android.settings.deviceinfo.storage.StorageUtils;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
+import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
+import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
+
+import java.util.Objects;
+
+/**
+ * Handles the option menu on the Storage settings.
+ */
+public class VolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
+ OnPrepareOptionsMenu, OnOptionsItemSelected {
+
+ @VisibleForTesting
+ MenuItem mRename;
+ @VisibleForTesting
+ MenuItem mMount;
+ @VisibleForTesting
+ MenuItem mUnmount;
+ @VisibleForTesting
+ MenuItem mFormat;
+ @VisibleForTesting
+ MenuItem mFormatAsPortable;
+ @VisibleForTesting
+ MenuItem mFormatAsInternal;
+ @VisibleForTesting
+ MenuItem mMigrate;
+ @VisibleForTesting
+ MenuItem mFree;
+ @VisibleForTesting
+ MenuItem mForget;
+
+ private final Context mContext;
+ private final Fragment mFragment;
+ private final PackageManager mPackageManager;
+ private final StorageManager mStorageManager;
+ private StorageEntry mStorageEntry;
+
+ public VolumeOptionMenuController(Context context, Fragment parent, StorageEntry storageEntry) {
+ mContext = context;
+ mFragment = parent;
+ mPackageManager = context.getPackageManager();
+ mStorageManager = context.getSystemService(StorageManager.class);
+ mStorageEntry = storageEntry;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+ inflater.inflate(R.menu.storage_volume, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ mRename = menu.findItem(R.id.storage_rename);
+ mMount = menu.findItem(R.id.storage_mount);
+ mUnmount = menu.findItem(R.id.storage_unmount);
+ mFormat = menu.findItem(R.id.storage_format);
+ mFormatAsPortable = menu.findItem(R.id.storage_format_as_portable);
+ mFormatAsInternal = menu.findItem(R.id.storage_format_as_internal);
+ mMigrate = menu.findItem(R.id.storage_migrate);
+ mFree = menu.findItem(R.id.storage_free);
+ mForget = menu.findItem(R.id.storage_forget);
+
+ mRename.setVisible(false);
+ mMount.setVisible(false);
+ mUnmount.setVisible(false);
+ mFormat.setVisible(false);
+ mFormatAsPortable.setVisible(false);
+ mFormatAsInternal.setVisible(false);
+ mMigrate.setVisible(false);
+ mFree.setVisible(false);
+ mForget.setVisible(false);
+
+ if (mStorageEntry.isDiskInfoUnsupported()) {
+ mFormat.setVisible(true);
+ return;
+ }
+ if (mStorageEntry.isVolumeRecordMissed()) {
+ mForget.setVisible(true);
+ return;
+ }
+ if (mStorageEntry.isUnmounted()) {
+ mMount.setVisible(true);
+ return;
+ }
+ if (!mStorageEntry.isMounted()) {
+ return;
+ }
+
+ if (mStorageEntry.isPrivate()) {
+ if (!mStorageEntry.isDefaultInternalStorage()) {
+ mRename.setVisible(true);
+ mUnmount.setVisible(true);
+ mFormatAsPortable.setVisible(true);
+ }
+
+ // Only offer to migrate when not current storage.
+ final VolumeInfo primaryVolumeInfo = mPackageManager.getPrimaryStorageCurrentVolume();
+ final VolumeInfo selectedVolumeInfo = mStorageEntry.getVolumeInfo();
+ mMigrate.setVisible(primaryVolumeInfo != null
+ && primaryVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
+ && !Objects.equals(selectedVolumeInfo, primaryVolumeInfo)
+ && primaryVolumeInfo.isMountedWritable());
+ return;
+ }
+
+ if (mStorageEntry.isPublic()) {
+ mRename.setVisible(true);
+ mUnmount.setVisible(true);
+ mFormat.setVisible(true);
+ final DiskInfo diskInfo = mStorageManager.findDiskById(mStorageEntry.getDiskId());
+ mFormatAsInternal.setVisible(diskInfo != null
+ && diskInfo.isAdoptable()
+ && UserManager.get(mContext).isAdminUser()
+ && !ActivityManager.isUserAMonkey());
+ return;
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ if (!mFragment.isAdded()) {
+ return false;
+ }
+
+ final int menuId = menuItem.getItemId();
+ if (menuId == R.id.storage_mount) {
+ if (mStorageEntry.isUnmounted()) {
+ new MountTask(mFragment.getActivity(), mStorageEntry.getVolumeInfo()).execute();
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_unmount) {
+ if (mStorageEntry.isMounted()) {
+ if (mStorageEntry.isPublic()) {
+ new UnmountTask(mFragment.getActivity(),
+ mStorageEntry.getVolumeInfo()).execute();
+ return true;
+ }
+ if (mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage()) {
+ final Bundle args = new Bundle();
+ args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+ new SubSettingLauncher(mContext)
+ .setDestination(PrivateVolumeUnmount.class.getCanonicalName())
+ .setTitleRes(R.string.storage_menu_unmount)
+ .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
+ .setArguments(args)
+ .launch();
+ return true;
+ }
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_rename) {
+ if ((mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage())
+ || mStorageEntry.isPublic()) {
+ StorageRenameFragment.show(mFragment, mStorageEntry.getVolumeInfo());
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_format) {
+ if (mStorageEntry.isDiskInfoUnsupported() || mStorageEntry.isPublic()) {
+ StorageWizardFormatConfirm.showPublic(mFragment.getActivity(),
+ mStorageEntry.getDiskId());
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_format_as_portable) {
+ if (mStorageEntry.isPrivate()) {
+ final Bundle args = new Bundle();
+ args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+ new SubSettingLauncher(mContext)
+ .setDestination(PrivateVolumeFormat.class.getCanonicalName())
+ .setTitleRes(R.string.storage_menu_format)
+ .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
+ .setArguments(args)
+ .launch();
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_format_as_internal) {
+ if (mStorageEntry.isPublic()) {
+ StorageWizardFormatConfirm.showPrivate(mFragment.getActivity(),
+ mStorageEntry.getDiskId());
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_migrate) {
+ if (mStorageEntry.isPrivate()) {
+ final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
+ intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+ mContext.startActivity(intent);
+ return true;
+ }
+ return false;
+ }
+ if (menuId == R.id.storage_forget) {
+ if (mStorageEntry.isVolumeRecordMissed()) {
+ StorageUtils.launchForgetMissingVolumeRecordFragment(mContext, mStorageEntry);
+ return true;
+ }
+ return false;
+ }
+ return false;
+ }
+
+ public void setSelectedStorageEntry(StorageEntry storageEntry) {
+ mStorageEntry = storageEntry;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionSettings.java b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionSettings.java
index 2ade3c2..e2d3d8a 100644
--- a/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionSettings.java
+++ b/src/com/android/settings/deviceinfo/firmwareversion/FirmwareVersionSettings.java
@@ -17,18 +17,12 @@
package com.android.settings.deviceinfo.firmwareversion;
import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
-import java.util.ArrayList;
-import java.util.List;
-
@SearchIndexable
public class FirmwareVersionSettings extends DashboardFragment {
diff --git a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFragment.java b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFragment.java
index adcbb2a..713eaf4 100644
--- a/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFragment.java
+++ b/src/com/android/settings/deviceinfo/hardwareinfo/HardwareInfoFragment.java
@@ -18,18 +18,12 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
-import java.util.ArrayList;
-import java.util.List;
-
-
@SearchIndexable
public class HardwareInfoFragment extends DashboardFragment {
diff --git a/src/com/android/settings/deviceinfo/legal/LegalPreferenceController.java b/src/com/android/settings/deviceinfo/legal/LegalPreferenceController.java
index 0e16474..9465fff 100644
--- a/src/com/android/settings/deviceinfo/legal/LegalPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/legal/LegalPreferenceController.java
@@ -27,7 +27,6 @@
import java.util.List;
-
public abstract class LegalPreferenceController extends BasePreferenceController {
private final PackageManager mPackageManager;
private Preference mPreference;
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index a6a211e..1e25179 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -44,6 +44,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
@@ -170,7 +171,8 @@
}
};
- private PhoneStateListener mPhoneStateListener;
+ @VisibleForTesting
+ protected SimStatusDialogTelephonyCallback mTelephonyCallback;
private CellBroadcastServiceConnection mCellBroadcastServiceConnection;
@@ -235,7 +237,7 @@
}
mTelephonyManager =
mTelephonyManager.createForSubscriptionId(mSubscriptionInfo.getSubscriptionId());
- mPhoneStateListener = getPhoneStateListener();
+ mTelephonyCallback = new SimStatusDialogTelephonyCallback();
updateLatestAreaInfo();
updateSubscriptionStatus();
}
@@ -278,11 +280,7 @@
}
mTelephonyManager = mTelephonyManager.createForSubscriptionId(
mSubscriptionInfo.getSubscriptionId());
- mTelephonyManager.listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_SERVICE_STATE
- | PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED);
+ mTelephonyManager.registerTelephonyCallback(mContext.getMainExecutor(), mTelephonyCallback);
mSubscriptionManager.addOnSubscriptionsChangedListener(
mContext.getMainExecutor(), mOnSubscriptionsChangedListener);
registerImsRegistrationCallback(mSubscriptionInfo.getSubscriptionId());
@@ -305,7 +303,7 @@
if (mIsRegisteredListener) {
mSubscriptionManager.removeOnSubscriptionsChangedListener(
mOnSubscriptionsChangedListener);
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
if (mShowLatestAreaInfo) {
mContext.unregisterReceiver(mAreaInfoReceiver);
}
@@ -316,7 +314,7 @@
unregisterImsRegistrationCallback(mSubscriptionInfo.getSubscriptionId());
mSubscriptionManager.removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
if (mShowLatestAreaInfo) {
mContext.unregisterReceiver(mAreaInfoReceiver);
@@ -768,33 +766,35 @@
}
@VisibleForTesting
- protected PhoneStateListener getPhoneStateListener() {
- return new PhoneStateListener() {
- @Override
- public void onDataConnectionStateChanged(int state) {
- updateDataState(state);
- updateNetworkType();
- }
+ class SimStatusDialogTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataConnectionStateListener,
+ TelephonyCallback.SignalStrengthsListener,
+ TelephonyCallback.ServiceStateListener,
+ TelephonyCallback.DisplayInfoListener {
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ updateDataState(state);
+ updateNetworkType();
+ }
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- updateSignalStrength(signalStrength);
- }
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ updateSignalStrength(signalStrength);
+ }
- @Override
- public void onServiceStateChanged(ServiceState serviceState) {
- updateNetworkProvider();
- updateServiceState(serviceState);
- updateRoamingStatus(serviceState);
- mPreviousServiceState = serviceState;
- }
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ updateNetworkProvider();
+ updateServiceState(serviceState);
+ updateRoamingStatus(serviceState);
+ mPreviousServiceState = serviceState;
+ }
- @Override
- public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo displayInfo) {
- mTelephonyDisplayInfo = displayInfo;
- updateNetworkType();
- }
- };
+ @Override
+ public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo displayInfo) {
+ mTelephonyDisplayInfo = displayInfo;
+ updateNetworkType();
+ }
}
@VisibleForTesting
diff --git a/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java b/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java
new file mode 100644
index 0000000..1e6a98d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.deviceinfo.StorageWizardInit;
+
+/** A dialog which guides users to initialize a specified unsupported disk. */
+public class DiskInitFragment extends InstrumentedDialogFragment {
+
+ private static final String TAG_DISK_INIT = "disk_init";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_VOLUME_INIT;
+ }
+
+ /** Shows the dialog for the specified diskId from DiskInfo. */
+ public static void show(Fragment parent, int resId, String diskId) {
+ final Bundle args = new Bundle();
+ args.putInt(Intent.EXTRA_TEXT, resId);
+ args.putString(DiskInfo.EXTRA_DISK_ID, diskId);
+
+ final DiskInitFragment dialog = new DiskInitFragment();
+ dialog.setArguments(args);
+ dialog.setTargetFragment(parent, 0);
+ dialog.show(parent.getFragmentManager(), TAG_DISK_INIT);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+ final int resId = getArguments().getInt(Intent.EXTRA_TEXT);
+ final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID);
+ final DiskInfo disk = storageManager.findDiskById(diskId);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ return builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription()))
+ .setPositiveButton(R.string.storage_menu_set_up, (dialog, which) -> {
+ final Intent intent = new Intent(context, StorageWizardInit.class);
+ intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
+ startActivity(intent); })
+ .setNegativeButton(R.string.cancel, null)
+ .create();
+ }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageEntry.java b/src/com/android/settings/deviceinfo/storage/StorageEntry.java
new file mode 100644
index 0000000..f718116
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageEntry.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.text.TextUtils;
+
+import java.io.File;
+
+/**
+ * This object contains a {@link VolumeInfo} for a mountable storage or a {@link DiskInfo} for an
+ * unsupported disk which is not able to be mounted automatically.
+ */
+public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
+
+ private final VolumeInfo mVolumeInfo;
+ private final DiskInfo mUnsupportedDiskInfo;
+ private final VolumeRecord mMissingVolumeRecord;
+
+ private final String mVolumeInfoDescription;
+
+ public StorageEntry(@NonNull Context context, @NonNull VolumeInfo volumeInfo) {
+ mVolumeInfo = volumeInfo;
+ mUnsupportedDiskInfo = null;
+ mMissingVolumeRecord = null;
+ mVolumeInfoDescription = context.getSystemService(StorageManager.class)
+ .getBestVolumeDescription(mVolumeInfo);
+ }
+
+ public StorageEntry(@NonNull DiskInfo diskInfo) {
+ mVolumeInfo = null;
+ mUnsupportedDiskInfo = diskInfo;
+ mMissingVolumeRecord = null;
+ mVolumeInfoDescription = null;
+ }
+
+ public StorageEntry(@NonNull VolumeRecord volumeRecord) {
+ mVolumeInfo = null;
+ mUnsupportedDiskInfo = null;
+ mMissingVolumeRecord = volumeRecord;
+ mVolumeInfoDescription = null;
+ }
+
+ private StorageEntry(Parcel in) {
+ mVolumeInfo = in.readParcelable(VolumeInfo.class.getClassLoader());
+ mUnsupportedDiskInfo = in.readParcelable(DiskInfo.class.getClassLoader());
+ mMissingVolumeRecord = in.readParcelable(VolumeRecord.class.getClassLoader());
+ mVolumeInfoDescription = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(mVolumeInfo, 0 /* parcelableFlags */);
+ out.writeParcelable(mUnsupportedDiskInfo, 0 /* parcelableFlags */);
+ out.writeParcelable(mMissingVolumeRecord , 0 /* parcelableFlags */);
+ out.writeString(mVolumeInfoDescription);
+ }
+
+ public static final Parcelable.Creator<StorageEntry> CREATOR =
+ new Parcelable.Creator<StorageEntry>() {
+ public StorageEntry createFromParcel(Parcel in) {
+ return new StorageEntry(in);
+ }
+
+ public StorageEntry[] newArray(int size) {
+ return new StorageEntry[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof StorageEntry)) {
+ return false;
+ }
+
+ final StorageEntry StorageEntry = (StorageEntry) o;
+ if (isVolumeInfo()) {
+ return mVolumeInfo.equals(StorageEntry.mVolumeInfo);
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);
+ }
+ return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);
+ }
+
+ @Override
+ public int hashCode() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.hashCode();
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.hashCode();
+ }
+ return mMissingVolumeRecord.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.toString();
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.toString();
+ }
+ return mMissingVolumeRecord.toString();
+ }
+
+ @Override
+ public int compareTo(StorageEntry other) {
+ if (isDefaultInternalStorage() && !other.isDefaultInternalStorage()) {
+ return -1;
+ }
+ if (!isDefaultInternalStorage() && other.isDefaultInternalStorage()) {
+ return 1;
+ }
+
+ if (isVolumeInfo() && !other.isVolumeInfo()) {
+ return -1;
+ }
+ if (!isVolumeInfo() && other.isVolumeInfo()) {
+ return 1;
+ }
+
+ if (isPrivate() && !other.isPrivate()) {
+ return -1;
+ }
+ if (!isPrivate() && other.isPrivate()) {
+ return 1;
+ }
+
+ if (isMounted() && !other.isMounted()) {
+ return -1;
+ }
+ if (!isMounted() && other.isMounted()) {
+ return 1;
+ }
+
+ if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {
+ return -1;
+ }
+ if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {
+ return 1;
+ }
+
+ if (getDescription() == null) {
+ return 1;
+ }
+ if (other.getDescription() == null) {
+ return -1;
+ }
+ return getDescription().compareTo(other.getDescription());
+ }
+
+ /** Returns default internal storage. */
+ public static StorageEntry getDefaultInternalStorageEntry(Context context) {
+ return new StorageEntry(context, context.getSystemService(StorageManager.class)
+ .findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+ }
+
+ /** If it's a VolumeInfo. */
+ public boolean isVolumeInfo() {
+ return mVolumeInfo != null;
+ }
+
+ /** If it's an unsupported DiskInfo. */
+ public boolean isDiskInfoUnsupported() {
+ return mUnsupportedDiskInfo != null;
+ }
+
+ /** If it's a missing VolumeRecord. */
+ public boolean isVolumeRecordMissed() {
+ return mMissingVolumeRecord != null;
+ }
+
+ /** If it's a default internal storage. */
+ public boolean isDefaultInternalStorage() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
+ && TextUtils.equals(mVolumeInfo.getId(), VolumeInfo.ID_PRIVATE_INTERNAL);
+ }
+ return false;
+ }
+
+ /** If it's a mounted storage. */
+ public boolean isMounted() {
+ return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED
+ || mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
+ }
+
+ /** If it's an unmounted storage. */
+ public boolean isUnmounted() {
+ return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);
+ }
+
+ /** If it's an unmountable storage. */
+ public boolean isUnmountable() {
+ return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;
+ }
+
+ /** If it's a private storage. */
+ public boolean isPrivate() {
+ return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;
+ }
+
+ /** If it's a public storage. */
+ public boolean isPublic() {
+ return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;
+ }
+
+ /** Returns description. */
+ public String getDescription() {
+ if (isVolumeInfo()) {
+ return mVolumeInfoDescription;
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.getDescription();
+ }
+ return mMissingVolumeRecord.getNickname();
+ }
+
+ /** Returns ID. */
+ public String getId() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.getId();
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.getId();
+ }
+ return mMissingVolumeRecord.getFsUuid();
+ }
+
+ /** Returns disk ID. */
+ public String getDiskId() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.getDiskId();
+ }
+ if (isDiskInfoUnsupported()) {
+ return mUnsupportedDiskInfo.getId();
+ }
+ return null;
+ }
+
+ /** Returns fsUuid. */
+ public String getFsUuid() {
+ if (isVolumeInfo()) {
+ return mVolumeInfo.getFsUuid();
+ }
+ if (isDiskInfoUnsupported()) {
+ return null;
+ }
+ return mMissingVolumeRecord.getFsUuid();
+ }
+
+ /** Returns root file if it's a VolumeInfo. */
+ public File getPath() {
+ return mVolumeInfo == null ? null : mVolumeInfo.getPath();
+ }
+
+ /** Returns VolumeInfo of the StorageEntry. */
+ public VolumeInfo getVolumeInfo() {
+ return mVolumeInfo;
+ }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index c2a0b62..dba72ba 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -158,6 +158,9 @@
intent = getAppsIntent();
break;
case FILES_KEY:
+ if (mVolume == null) {
+ break;
+ }
intent = getFilesIntent();
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(
mContext, SettingsEnums.STORAGE_FILES);
@@ -293,6 +296,17 @@
mTotalSize = totalSizeBytes;
}
+ /** Set storage size to 0 for each preference. */
+ public void clearStorageSizeDisplay() {
+ mPhotoPreference.setStorageSize(0L, 0L);
+ mAudioPreference.setStorageSize(0L, 0L);
+ mGamePreference.setStorageSize(0L, 0L);
+ mMoviesPreference.setStorageSize(0L, 0L);
+ mAppPreference.setStorageSize(0L, 0L);
+ mFilePreference.setStorageSize(0L, 0L);
+ mSystemPreference.setStorageSize(0L, 0L);
+ }
+
/**
* Returns a list of keys used by this preference controller.
*/
diff --git a/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java b/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java
new file mode 100644
index 0000000..c67fe33
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Dialog that allows editing of volume nickname.
+ */
+public class StorageRenameFragment extends InstrumentedDialogFragment {
+ private static final String TAG_RENAME = "rename";
+
+ /** Shows the rename dialog. */
+ public static void show(Fragment parent, VolumeInfo vol) {
+ final StorageRenameFragment dialog = new StorageRenameFragment();
+ dialog.setTargetFragment(parent, 0 /* requestCode */);
+ final Bundle args = new Bundle();
+ args.putString(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid());
+ dialog.setArguments(args);
+ dialog.show(parent.getFragmentManager(), TAG_RENAME);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_VOLUME_RENAME;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+ final String fsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID);
+ final VolumeRecord rec = storageManager.findRecordByUuid(fsUuid);
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+ final View view = dialogInflater.inflate(R.layout.dialog_edittext, null, false);
+ final EditText nickname = (EditText) view.findViewById(R.id.edittext);
+ nickname.setText(rec.getNickname());
+
+ return builder.setTitle(R.string.storage_rename_title)
+ .setView(view)
+ .setPositiveButton(R.string.save, (dialog, which) ->
+ // TODO: move to background thread
+ storageManager.setVolumeNickname(fsUuid, nickname.getText().toString()))
+ .setNegativeButton(R.string.cancel, null)
+ .create();
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
new file mode 100644
index 0000000..03fddec
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.SettingsSpinnerPreference;
+import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Shows a spinner for users to select a storage volume.
+ */
+public class StorageSelectionPreferenceController extends BasePreferenceController implements
+ AdapterView.OnItemSelectedListener {
+
+ @VisibleForTesting
+ SettingsSpinnerPreference mSpinnerPreference;
+ @VisibleForTesting
+ StorageAdapter mStorageAdapter;
+
+ private final List<StorageEntry> mStorageEntries = new ArrayList<>();
+
+ /** The interface for spinner selection callback. */
+ public interface OnItemSelectedListener {
+ /** Callbacked when the spinner selection is changed. */
+ void onItemSelected(StorageEntry storageEntry);
+ }
+ private OnItemSelectedListener mOnItemSelectedListener;
+
+ public StorageSelectionPreferenceController(Context context, String key) {
+ super(context, key);
+
+ mStorageAdapter = new StorageAdapter(context);
+ }
+
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ }
+
+ /** Set the storages in the spinner. */
+ public void setStorageEntries(List<StorageEntry> storageEntries) {
+ mStorageAdapter.clear();
+ mStorageEntries.clear();
+ if (storageEntries == null || storageEntries.isEmpty()) {
+ return;
+ }
+ Collections.sort(mStorageEntries);
+ mStorageEntries.addAll(storageEntries);
+ mStorageAdapter.addAll(storageEntries);
+ }
+
+ /** set selected storage in the spinner. */
+ public void setSelectedStorageEntry(StorageEntry selectedStorageEntry) {
+ if (mSpinnerPreference == null || !mStorageEntries.contains(selectedStorageEntry)) {
+ return;
+ }
+ mSpinnerPreference.setSelection(mStorageAdapter.getPosition(selectedStorageEntry));
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mSpinnerPreference = screen.findPreference(getPreferenceKey());
+ mSpinnerPreference.setAdapter(mStorageAdapter);
+ mSpinnerPreference.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
+ if (mOnItemSelectedListener == null) {
+ return;
+ }
+ mOnItemSelectedListener.onItemSelected(mStorageAdapter.getItem(position));
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> arg0) {
+ // Do nothing.
+ }
+
+ @VisibleForTesting
+ class StorageAdapter extends SettingsSpinnerAdapter<StorageEntry> {
+
+ StorageAdapter(Context context) {
+ super(context);
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = getDefaultView(position, view, parent);
+ }
+
+ TextView textView = null;
+ try {
+ textView = (TextView) view;
+ } catch (ClassCastException e) {
+ throw new IllegalStateException("Default view should be a TextView, ", e);
+ }
+ textView.setText(getItem(position).getDescription());
+ return textView;
+ }
+
+ @Override
+ public View getDropDownView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = getDefaultDropDownView(position, view, parent);
+ }
+
+ TextView textView = null;
+ try {
+ textView = (TextView) view;
+ } catch (ClassCastException e) {
+ throw new IllegalStateException("Default drop down view should be a TextView, ", e);
+ }
+ textView.setText(getItem(position).getDescription());
+ return textView;
+ }
+ }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
new file mode 100644
index 0000000..a00b25a
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.UsageProgressBarPreference;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Shows storage summary and progress.
+ */
+public class StorageUsageProgressBarPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "StorageProgressCtrl";
+
+ private final StorageStatsManager mStorageStatsManager;
+ @VisibleForTesting
+ long mUsedBytes;
+ @VisibleForTesting
+ long mTotalBytes;
+ private UsageProgressBarPreference mUsageProgressBarPreference;
+ private StorageEntry mStorageEntry;
+
+ public StorageUsageProgressBarPreferenceController(Context context, String key) {
+ super(context, key);
+
+ mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
+ }
+
+ /** Set StorageEntry to display. */
+ public void setSelectedStorageEntry(StorageEntry storageEntry) {
+ mStorageEntry = storageEntry;
+ getStorageStatsAndUpdateUi();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mUsageProgressBarPreference = screen.findPreference(getPreferenceKey());
+ getStorageStatsAndUpdateUi();
+ }
+
+ private void getStorageStatsAndUpdateUi() {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ try {
+ if (mStorageEntry == null || !mStorageEntry.isMounted()) {
+ throw new IOException();
+ }
+
+ if (mStorageEntry.isPrivate()) {
+ // StorageStatsManager can only query private storages.
+ mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
+ mUsedBytes = mTotalBytes
+ - mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
+ } else {
+ final File rootFile = mStorageEntry.getPath();
+ if (rootFile == null) {
+ Log.d(TAG, "Mounted public storage has null root path: " + mStorageEntry);
+ throw new IOException();
+ }
+ mTotalBytes = rootFile.getTotalSpace();
+ mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
+ }
+ } catch (IOException e) {
+ // The storage device isn't present.
+ mTotalBytes = 0;
+ mUsedBytes = 0;
+ }
+
+ if (mUsageProgressBarPreference == null) {
+ return;
+ }
+ ThreadUtils.postOnMainThread(() ->
+ updateState(mUsageProgressBarPreference)
+ );
+ });
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ mUsageProgressBarPreference.setUsageSummary(
+ getStorageSummary(R.string.storage_usage_summary, mUsedBytes));
+ mUsageProgressBarPreference.setTotalSummary(
+ getStorageSummary(R.string.storage_total_summary, mTotalBytes));
+ mUsageProgressBarPreference.setPercent(mUsedBytes, mTotalBytes);
+ }
+
+ private String getStorageSummary(int resId, long bytes) {
+ final Formatter.BytesResult result = Formatter.formatBytes(mContext.getResources(),
+ bytes, 0);
+ return mContext.getString(resId, result.value, result.units);
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUtils.java b/src/com/android/settings/deviceinfo/storage/StorageUtils.java
new file mode 100644
index 0000000..26bdec0
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.storage.VolumeRecord;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.deviceinfo.PrivateVolumeForget;
+
+/** Storage utilities */
+public class StorageUtils {
+
+ /** Launches the fragment to forget a specified missing volume record. */
+ public static void launchForgetMissingVolumeRecordFragment(Context context,
+ StorageEntry storageEntry) {
+ if (storageEntry == null || !storageEntry.isVolumeRecordMissed()) {
+ return;
+ }
+
+ final Bundle args = new Bundle();
+ args.putString(VolumeRecord.EXTRA_FS_UUID, storageEntry.getFsUuid());
+ new SubSettingLauncher(context)
+ .setDestination(PrivateVolumeForget.class.getCanonicalName())
+ .setTitleRes(R.string.storage_menu_forget)
+ .setSourceMetricsCategory(SettingsEnums.SETTINGS_STORAGE_CATEGORY)
+ .setArguments(args)
+ .launch();
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/VolumeSizesLoader.java b/src/com/android/settings/deviceinfo/storage/VolumeSizesLoader.java
index d95befa..64510c6 100644
--- a/src/com/android/settings/deviceinfo/storage/VolumeSizesLoader.java
+++ b/src/com/android/settings/deviceinfo/storage/VolumeSizesLoader.java
@@ -26,6 +26,7 @@
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import com.android.settingslib.utils.AsyncLoaderCompat;
+import java.io.File;
import java.io.IOException;
public class VolumeSizesLoader extends AsyncLoaderCompat<PrivateStorageInfo> {
@@ -49,6 +50,11 @@
@Override
public PrivateStorageInfo loadInBackground() {
+ if (mVolume == null || (mVolume.getState() != VolumeInfo.STATE_MOUNTED
+ && mVolume.getState() != VolumeInfo.STATE_MOUNTED_READ_ONLY)) {
+ return new PrivateStorageInfo(0L /* freeBytes */, 0L /* totalBytes */);
+ }
+
PrivateStorageInfo volumeSizes;
try {
volumeSizes = getVolumeSize(mVolumeProvider, mStats, mVolume);
@@ -62,8 +68,14 @@
static PrivateStorageInfo getVolumeSize(
StorageVolumeProvider storageVolumeProvider, StorageStatsManager stats, VolumeInfo info)
throws IOException {
- long privateTotalBytes = storageVolumeProvider.getTotalBytes(stats, info);
- long privateFreeBytes = storageVolumeProvider.getFreeBytes(stats, info);
- return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes);
+ if (info.getType() == VolumeInfo.TYPE_PRIVATE) {
+ return new PrivateStorageInfo(storageVolumeProvider.getFreeBytes(stats, info),
+ storageVolumeProvider.getTotalBytes(stats, info));
+ }
+ // TODO(b/174964885): It's confusing to use PrivateStorageInfo for a public storage,
+ // replace it with a new naming or a different object.
+ final File rootFile = info.getPath();
+ return rootFile == null ? new PrivateStorageInfo(0L /* freeBytes */, 0L /* totalBytes */)
+ : new PrivateStorageInfo(rootFile.getFreeSpace(), rootFile.getTotalSpace());
}
}
diff --git a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
index 5118b27..7b3d788 100644
--- a/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
+++ b/src/com/android/settings/display/TopLevelWallpaperPreferenceController.java
@@ -105,7 +105,7 @@
final Intent intent = new Intent().setComponent(
getComponentName()).putExtra(mWallpaperLaunchExtra, LAUNCHED_SETTINGS);
if (areStylesAvailable()) {
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
preference.getContext().startActivity(intent);
return true;
diff --git a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
index cf23c94..1140291 100644
--- a/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
+++ b/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelper.java
@@ -141,17 +141,14 @@
}
private boolean isNotCurrentUserOrProfile(ComponentName admin, int userId) {
- return !isFinancedDevice()
- && (!RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(mActivity, admin)
- || !RestrictedLockUtils.isCurrentUserOrProfile(mActivity, userId));
+ return !RestrictedLockUtilsInternal.isAdminInCurrentUserOrProfile(mActivity, admin)
+ || !RestrictedLockUtils.isCurrentUserOrProfile(mActivity, userId);
}
@VisibleForTesting
void setAdminSupportIcon(View root, ComponentName admin, int userId) {
ImageView supportIconView = root.requireViewById(R.id.admin_support_icon);
- if (isFinancedDevice()) {
- supportIconView.setVisibility(View.GONE);
- } else if (isNotCurrentUserOrProfile(admin, userId)) {
+ if (isNotCurrentUserOrProfile(admin, userId)) {
supportIconView.setImageDrawable(
mActivity.getDrawable(com.android.internal.R.drawable.ic_info));
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index e8d5f33..399a84d 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -23,7 +23,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.os.BatteryStats;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -33,9 +32,6 @@
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.internal.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
@@ -101,63 +97,46 @@
private String mPackageName;
- @VisibleForTesting
- static void startBatteryDetailPage(Activity caller, BatteryUtils batteryUtils,
- InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
- BatteryEntry entry, String usagePercent) {
- // Initialize mStats if necessary.
- helper.getStats();
-
+ /**
+ * Launches battery details page for an individual battery consumer.
+ */
+ public static void startBatteryDetailPage(Activity caller,
+ InstrumentedPreferenceFragment fragment, BatteryEntry entry, String usagePercent) {
final Bundle args = new Bundle();
- final BatterySipper sipper = entry.sipper;
- final BatteryStats.Uid uid = sipper.uidObj;
- final boolean isTypeApp = sipper.drainType == BatterySipper.DrainType.APP;
-
- final long foregroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
- BatteryUtils.StatusType.FOREGROUND, uid, which) : sipper.usageTimeMs;
- final long backgroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
- BatteryUtils.StatusType.BACKGROUND, uid, which) : 0;
-
- if (ArrayUtils.isEmpty(sipper.mPackages)) {
+ final long foregroundTimeMs = entry.getTimeInForegroundMs();
+ final long backgroundTimeMs = entry.getTimeInBackgroundMs();
+ final String packageName = entry.getDefaultPackageName();
+ if (packageName == null) {
// populate data for system app
args.putString(EXTRA_LABEL, entry.getLabel());
args.putInt(EXTRA_ICON_ID, entry.iconId);
args.putString(EXTRA_PACKAGE_NAME, null);
} else {
// populate data for normal app
- args.putString(EXTRA_PACKAGE_NAME, entry.defaultPackageName != null
- ? entry.defaultPackageName
- : sipper.mPackages[0]);
+ args.putString(EXTRA_PACKAGE_NAME, packageName);
}
- args.putInt(EXTRA_UID, sipper.getUid());
+ args.putInt(EXTRA_UID, entry.getUid());
args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs);
args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
- args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
+ args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) entry.getConsumedPower());
new SubSettingLauncher(caller)
.setDestination(AdvancedPowerUsageDetail.class.getName())
.setTitleRes(R.string.battery_details_title)
.setArguments(args)
.setSourceMetricsCategory(fragment.getMetricsCategory())
- .setUserHandle(new UserHandle(getUserIdToLaunchAdvancePowerUsageDetail(sipper)))
+ .setUserHandle(new UserHandle(getUserIdToLaunchAdvancePowerUsageDetail(entry)))
.launch();
}
- private static @UserIdInt
- int getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs) {
- if (bs.drainType == BatterySipper.DrainType.USER) {
+ private static @UserIdInt int getUserIdToLaunchAdvancePowerUsageDetail(
+ BatteryEntry batteryEntry) {
+ if (batteryEntry.isUserEntry()) {
return ActivityManager.getCurrentUser();
}
- return UserHandle.getUserId(bs.getUid());
- }
-
- public static void startBatteryDetailPage(Activity caller,
- InstrumentedPreferenceFragment fragment, BatteryStatsHelper helper, int which,
- BatteryEntry entry, String usagePercent) {
- startBatteryDetailPage(caller, BatteryUtils.getInstance(caller), fragment, helper, which,
- entry, usagePercent);
+ return UserHandle.getUserId(batteryEntry.getUid());
}
public static void startBatteryDetailPage(Activity caller,
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 1a9db03..47b2a0a 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -19,18 +19,22 @@
import android.app.Activity;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.os.BatteryStats;
+import android.os.BatteryConsumer;
+import android.os.BatteryUsageStats;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.SparseArray;
import androidx.annotation.VisibleForTesting;
@@ -38,9 +42,6 @@
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
-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;
@@ -65,35 +66,57 @@
static final boolean USE_FAKE_DATA = false;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 20;
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 BatteryUsageStats mBatteryUsageStats;
private ArrayMap<String, Preference> mPreferenceCache;
@VisibleForTesting
BatteryUtils mBatteryUtils;
- private UserManager mUserManager;
- private SettingsActivity mActivity;
- private InstrumentedPreferenceFragment mFragment;
+ private final UserManager mUserManager;
+ private final PackageManager mPackageManager;
+ private final SettingsActivity mActivity;
+ private final InstrumentedPreferenceFragment mFragment;
private Context mPrefContext;
- private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ /**
+ * Battery attribution list configuration.
+ */
+ public interface Config {
+ /**
+ * Returns true if the attribution list should be shown.
+ */
+ boolean shouldShowBatteryAttributionList(Context context);
+ }
+
+ @VisibleForTesting
+ static Config sConfig = new Config() {
+ @Override
+ public boolean shouldShowBatteryAttributionList(Context context) {
+ if (USE_FAKE_DATA) {
+ return true;
+ }
+
+ PowerProfile powerProfile = new PowerProfile(context);
+ return powerProfile.getAveragePower(PowerProfile.POWER_SCREEN_FULL)
+ >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
+ }
+ };
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@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()));
+ PowerGaugePreference pgp = mAppListGroup.findPreference(entry.getKey());
if (pgp != null) {
- final int userId = UserHandle.getUserId(entry.sipper.getUid());
+ final int userId = UserHandle.getUserId(entry.getUid());
final UserHandle userHandle = new UserHandle(userId);
pgp.setIcon(mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle));
pgp.setTitle(entry.name);
- if (entry.sipper.drainType == DrainType.APP) {
+ if (entry.isAppEntry()) {
pgp.setContentDescription(entry.name);
}
}
@@ -121,6 +144,7 @@
mPreferenceKey = preferenceKey;
mBatteryUtils = BatteryUtils.getInstance(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mPackageManager = context.getPackageManager();
mActivity = activity;
mFragment = fragment;
}
@@ -160,78 +184,63 @@
if (preference instanceof PowerGaugePreference) {
PowerGaugePreference pgp = (PowerGaugePreference) preference;
BatteryEntry entry = pgp.getInfo();
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils,
- mFragment, mBatteryStatsHelper, STATS_TYPE, entry, pgp.getPercent());
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
+ mFragment, entry, pgp.getPercent());
return true;
}
return false;
}
- public void refreshAppListGroup(BatteryStatsHelper statsHelper, boolean showAllApps) {
+ /**
+ * Refreshes the list of battery consumers using the supplied BatteryUsageStats.
+ */
+ public void refreshAppListGroup(BatteryUsageStats batteryUsageStats, boolean showAllApps) {
if (!isAvailable()) {
return;
}
- mBatteryStatsHelper = statsHelper;
+ mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
mAppListGroup.setTitle(R.string.power_usage_list_summary);
- 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);
-
+ if (sConfig.shouldShowBatteryAttributionList(mContext)) {
+ final int dischargePercentage = getDischargePercentage(batteryUsageStats);
+ final List<BatteryEntry> usageList = getCoalescedUsageList(showAllApps);
+ final double totalPower = batteryUsageStats.getConsumedPower();
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 BatteryEntry entry = usageList.get(i);
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
- sipper.totalPowerMah, totalPower, hiddenPowerMah, dischargeAmount);
+ entry.getConsumedPower(), totalPower, dischargePercentage);
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, null);
+
+ final UserHandle userHandle = new UserHandle(UserHandle.getUserId(entry.getUid()));
final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
userHandle);
final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
- entry.getLabel(),
- userHandle);
+ entry.getLabel(), userHandle);
- final String key = extractKeyFromSipper(sipper);
+ final String key = entry.getKey();
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
if (pref == null) {
pref = new PowerGaugePreference(mPrefContext, badgedIcon,
contentDescription, entry);
pref.setKey(key);
}
- sipper.percent = percentOfTotal;
+ entry.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);
+ setUsageSummary(pref, entry);
addedSome = true;
mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() - getCachedCount()
@@ -248,6 +257,14 @@
BatteryEntry.startRequestQueue();
}
+ private int getDischargePercentage(BatteryUsageStats batteryUsageStats) {
+ int dischargePercentage = batteryUsageStats.getDischargePercentage();
+ if (dischargePercentage < 0) {
+ dischargePercentage = 0;
+ }
+ return dischargePercentage;
+ }
+
/**
* 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
@@ -255,129 +272,102 @@
*
* @return A sorted list of apps using power.
*/
- private List<BatterySipper> getCoalescedUsageList(final List<BatterySipper> sippers) {
- final SparseArray<BatterySipper> uidList = new SparseArray<>();
+ private List<BatteryEntry> getCoalescedUsageList(boolean showAllApps) {
+ final SparseArray<BatteryEntry> batteryEntryList = 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();
+ final ArrayList<BatteryEntry> results = new ArrayList<>();
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ mBatteryUsageStats.getUidBatteryConsumers();
+ for (int i = 0, size = uidBatteryConsumers.size(); i < size; i++) {
+ final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
+ int realUid = consumer.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 shared GID. If so, we combine it with the OWNER's
+ // actual app UID.
+ if (isSharedGid(consumer.getUid())) {
+ realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
+ UserHandle.getAppIdFromSharedAppGid(consumer.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;
- }
+ // Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
+ if (isSystemUid(realUid)
+ && !"mediaserver".equals(consumer.getPackageWithHighestDrain())) {
+ // 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;
- }
+ final String[] packages = mPackageManager.getPackagesForUid(consumer.getUid());
+ if (mBatteryUtils.shouldHideUidBatteryConsumerUnconditionally(consumer, packages)) {
+ continue;
+ }
- 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 boolean isHidden = mBatteryUtils.shouldHideUidBatteryConsumer(consumer, packages);
+ if (isHidden && !showAllApps) {
+ continue;
+ }
- 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;
- }
- }
+ final int index = batteryEntryList.indexOfKey(realUid);
+ if (index < 0) {
+ // New entry.
+ batteryEntryList.put(realUid, new BatteryEntry(mActivity, mHandler, mUserManager,
+ consumer, isHidden, packages, null));
} else {
- results.add(sipper);
+ // Combine BatterySippers if we already have one with this UID.
+ final BatteryEntry existingSipper = batteryEntryList.valueAt(index);
+ existingSipper.add(consumer);
}
}
- final int numUidSippers = uidList.size();
+ final List<SystemBatteryConsumer> systemBatteryConsumers =
+ mBatteryUsageStats.getSystemBatteryConsumers();
+ for (int i = 0, size = systemBatteryConsumers.size(); i < size; i++) {
+ final SystemBatteryConsumer consumer = systemBatteryConsumers.get(i);
+ if (!showAllApps && mBatteryUtils.shouldHideSystemBatteryConsumer(consumer)) {
+ continue;
+ }
+
+ results.add(new BatteryEntry(mActivity, mHandler, mUserManager,
+ consumer, /* isHidden */ true, null, null));
+ }
+
+ if (showAllApps) {
+ final List<UserBatteryConsumer> userBatteryConsumers =
+ mBatteryUsageStats.getUserBatteryConsumers();
+ for (int i = 0, size = userBatteryConsumers.size(); i < size; i++) {
+ final UserBatteryConsumer consumer = userBatteryConsumers.get(i);
+ results.add(new BatteryEntry(mActivity, mHandler, mUserManager,
+ consumer, /* isHidden */ true, null, null));
+ }
+ }
+
+ final int numUidSippers = batteryEntryList.size();
+
for (int i = 0; i < numUidSippers; i++) {
- results.add(uidList.valueAt(i));
+ results.add(batteryEntryList.valueAt(i));
}
// The sort order must have changed, so re-sort based on total power use.
- mBatteryUtils.sortUsageList(results);
+ results.sort(BatteryEntry.COMPARATOR);
return results;
}
@VisibleForTesting
- void setUsageSummary(Preference preference, BatterySipper sipper) {
+ void setUsageSummary(Preference preference, BatteryEntry entry) {
// Only show summary when usage time is longer than one minute
- final long usageTimeMs = sipper.usageTimeMs;
- if (shouldShowSummary(sipper) && usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
+ final long usageTimeMs = entry.getTimeInForegroundMs();
+ if (shouldShowSummary(entry) && usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
final CharSequence timeSequence =
StringUtil.formatElapsedTime(mContext, usageTimeMs, false);
preference.setSummary(
- (sipper.drainType != DrainType.APP || mBatteryUtils.shouldHideSipper(sipper))
+ entry.isHidden()
? timeSequence
: TextUtils.expandTemplate(mContext.getText(R.string.battery_used_for),
timeSequence));
}
}
- @VisibleForTesting
- boolean shouldHideSipper(BatterySipper sipper) {
- // Don't show over-counted, unaccounted and hidden system module in any condition
- return sipper.drainType == BatterySipper.DrainType.OVERCOUNTED
- || sipper.drainType == BatterySipper.DrainType.UNACCOUNTED
- || mBatteryUtils.isHiddenSystemModule(sipper) || sipper.getUid() < 0;
- }
-
- @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();
@@ -390,12 +380,12 @@
}
}
- private boolean shouldShowSummary(BatterySipper sipper) {
+ private boolean shouldShowSummary(BatteryEntry entry) {
final CharSequence[] allowlistPackages = mContext.getResources()
.getTextArray(R.array.allowlist_hide_summary_in_battery_usage);
- final String target = sipper.packageWithHighestDrain;
+ final String target = entry.getDefaultPackageName();
- for (CharSequence packageName: allowlistPackages) {
+ for (CharSequence packageName : allowlistPackages) {
if (TextUtils.equals(target, packageName)) {
return false;
}
@@ -412,39 +402,54 @@
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));
+ private BatteryUsageStats getFakeStats() {
+ BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(0, 0)
+ .setDischargePercentage(100);
+
+ float use = 500;
+ for (@SystemBatteryConsumer.DrainType int drainType : new int[]{
+ SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY,
+ SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH,
+ SystemBatteryConsumer.DRAIN_TYPE_CAMERA,
+ SystemBatteryConsumer.DRAIN_TYPE_FLASHLIGHT,
+ SystemBatteryConsumer.DRAIN_TYPE_IDLE,
+ SystemBatteryConsumer.DRAIN_TYPE_MEMORY,
+ SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO,
+ SystemBatteryConsumer.DRAIN_TYPE_PHONE,
+ SystemBatteryConsumer.DRAIN_TYPE_SCREEN,
+ SystemBatteryConsumer.DRAIN_TYPE_WIFI,
+ }) {
+ builder.getOrCreateSystemBatteryConsumerBuilder(drainType)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, use);
use += 5;
}
+
+ use = 450;
for (int i = 0; i < 100; i++) {
- stats.add(new BatterySipper(DrainType.APP,
- new FakeUid(Process.FIRST_APPLICATION_UID + i), use));
+ builder.getOrCreateUidBatteryConsumerBuilder(
+ new FakeUid(Process.FIRST_APPLICATION_UID + i))
+ .setTimeInStateMs(BatteryConsumer.TIME_COMPONENT_USAGE, 10000 + i * 1000)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, use);
+ use += 1;
}
- 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);
+ builder.getOrCreateUidBatteryConsumerBuilder(new FakeUid(Process.FIRST_APPLICATION_UID))
+ .setTimeInStateMs(BatteryConsumer.TIME_COMPONENT_USAGE, 100000)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 1000.0)
+ .setPackageWithHighestDrain("dex2oat");
- sipper = new BatterySipper(DrainType.APP,
- new FakeUid(UserHandle.getSharedAppGid(Process.FIRST_APPLICATION_UID + 1)), 10.0f);
- sipper.packageWithHighestDrain = "dex2oat";
- stats.add(sipper);
+ builder.getOrCreateUidBatteryConsumerBuilder(new FakeUid(Process.FIRST_APPLICATION_UID + 1))
+ .setTimeInStateMs(BatteryConsumer.TIME_COMPONENT_USAGE, 100000)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 1000.0)
+ .setPackageWithHighestDrain("dex2oat");
- sipper = new BatterySipper(DrainType.APP,
- new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)), 9.0f);
- stats.add(sipper);
+ builder.getOrCreateUidBatteryConsumerBuilder(
+ new FakeUid(UserHandle.getSharedAppGid(Process.LOG_UID)))
+ .setTimeInStateMs(BatteryConsumer.TIME_COMPONENT_USAGE, 100000)
+ .setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 900.0);
- return stats;
+ return builder.build();
}
private Preference getCachedPreference(String key) {
diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java
index d533c80..9fafefd 100644
--- a/src/com/android/settings/fuelgauge/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryEntry.java
@@ -25,19 +25,24 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
+import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemBatteryConsumer;
import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import com.android.internal.os.BatterySipper;
+import androidx.annotation.NonNull;
+
import com.android.settings.R;
import com.android.settingslib.Utils;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
@@ -52,9 +57,9 @@
private static final String TAG = "BatteryEntry";
private static final String PACKAGE_SYSTEM = "android";
- static final HashMap<String,UidToDetail> sUidCache = new HashMap<String,UidToDetail>();
+ static final HashMap<String, UidToDetail> sUidCache = new HashMap<>();
- static final ArrayList<BatteryEntry> mRequestQueue = new ArrayList<BatteryEntry>();
+ static final ArrayList<BatteryEntry> sRequestQueue = new ArrayList<BatteryEntry>();
static Handler sHandler;
static Locale sCurrentLocale = null;
@@ -74,15 +79,14 @@
public void run() {
while (true) {
BatteryEntry be;
- synchronized (mRequestQueue) {
- if (mRequestQueue.isEmpty() || mAbort) {
+ synchronized (sRequestQueue) {
+ if (sRequestQueue.isEmpty() || mAbort) {
if (sHandler != null) {
sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
}
- mRequestQueue.clear();
return;
}
- be = mRequestQueue.remove(0);
+ be = sRequestQueue.remove(0);
}
be.loadNameAndIcon();
}
@@ -93,25 +97,26 @@
public static void startRequestQueue() {
if (sHandler != null) {
- synchronized (mRequestQueue) {
- if (!mRequestQueue.isEmpty()) {
+ synchronized (sRequestQueue) {
+ if (!sRequestQueue.isEmpty()) {
if (mRequestThread != null) {
mRequestThread.abort();
}
mRequestThread = new NameAndIconLoader();
mRequestThread.setPriority(Thread.MIN_PRIORITY);
mRequestThread.start();
- mRequestQueue.notify();
+ sRequestQueue.notify();
}
}
}
}
public static void stopRequestQueue() {
- synchronized (mRequestQueue) {
+ synchronized (sRequestQueue) {
if (mRequestThread != null) {
mRequestThread.abort();
mRequestThread = null;
+ sRequestQueue.clear();
sHandler = null;
}
}
@@ -121,14 +126,19 @@
sUidCache.clear();
}
- public final Context context;
- public final BatterySipper sipper;
- public final UidBatteryConsumer uidBatteryConsumer;
+ public static final Comparator<BatteryEntry> COMPARATOR =
+ (a, b) -> Double.compare(b.getConsumedPower(), a.getConsumedPower());
+
+ private final Context mContext;
+ private final BatteryConsumer mBatteryConsumer;
+ private final boolean mIsHidden;
public String name;
public Drawable icon;
public int iconId; // For passing to the detail screen.
- public String defaultPackageName;
+ public double percent;
+ private String mDefaultPackageName;
+ private double mConsumedPower;
static class UidToDetail {
String name;
@@ -136,123 +146,100 @@
Drawable icon;
}
- public BatteryEntry(Context context, Handler handler, UserManager um, BatterySipper sipper,
- UidBatteryConsumer uidBatteryConsumer) {
+ public BatteryEntry(Context context, Handler handler, UserManager um,
+ @NonNull BatteryConsumer batteryConsumer, boolean isHidden, String[] packages,
+ String packageName) {
sHandler = handler;
- this.context = context;
- this.sipper = sipper;
- this.uidBatteryConsumer = uidBatteryConsumer;
+ mContext = context;
+ mBatteryConsumer = batteryConsumer;
+ mIsHidden = isHidden;
+ mDefaultPackageName = packageName;
+ mConsumedPower = batteryConsumer.getConsumedPower();
- // This condition is met when BatteryEntry is initialized from BatteryUsageStats.
- // Once the conversion from BatteryStatsHelper is completed, the condition will
- // always be true and can be removed.
- if (uidBatteryConsumer != null) {
- PackageManager pm = context.getPackageManager();
+ if (batteryConsumer instanceof UidBatteryConsumer) {
+ UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
int uid = uidBatteryConsumer.getUid();
- String[] packages = pm.getPackagesForUid(uid);
- // Apps should only have one package
- if (packages == null || packages.length != 1) {
- name = uidBatteryConsumer.getPackageWithHighestDrain();
- } else {
- defaultPackageName = packages[0];
+ if (mDefaultPackageName == null) {
+ // Apps should only have one package
+ if (packages != null && packages.length == 1) {
+ mDefaultPackageName = packages[0];
+ } else {
+ mDefaultPackageName = uidBatteryConsumer.getPackageWithHighestDrain();
+ }
+ }
+ if (mDefaultPackageName != null) {
+ PackageManager pm = context.getPackageManager();
try {
ApplicationInfo appInfo =
- pm.getApplicationInfo(defaultPackageName, 0 /* no flags */);
+ pm.getApplicationInfo(mDefaultPackageName, 0 /* no flags */);
name = pm.getApplicationLabel(appInfo).toString();
} catch (NameNotFoundException e) {
Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: "
- + defaultPackageName);
- name = defaultPackageName;
+ + mDefaultPackageName);
+ name = mDefaultPackageName;
}
}
- if ((name == null || iconId == 0) && uid != 0) {
- getQuickNameIconForUid(uid);
- }
+ getQuickNameIconForUid(uid, packages);
return;
+ } else if (batteryConsumer instanceof SystemBatteryConsumer) {
+ switch(((SystemBatteryConsumer) batteryConsumer).getDrainType()) {
+ case SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY:
+ name = context.getResources().getString(R.string.ambient_display_screen_title);
+ iconId = R.drawable.ic_settings_aod;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH:
+ name = context.getResources().getString(R.string.power_bluetooth);
+ iconId = com.android.internal.R.drawable.ic_settings_bluetooth;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_CAMERA:
+ name = context.getResources().getString(R.string.power_camera);
+ iconId = R.drawable.ic_settings_camera;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO:
+ name = context.getResources().getString(R.string.power_cell);
+ iconId = R.drawable.ic_cellular_1_bar;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_FLASHLIGHT:
+ name = context.getResources().getString(R.string.power_flashlight);
+ iconId = R.drawable.ic_settings_display;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_PHONE:
+ name = context.getResources().getString(R.string.power_phone);
+ iconId = R.drawable.ic_settings_voice_calls;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_SCREEN:
+ name = context.getResources().getString(R.string.power_screen);
+ iconId = R.drawable.ic_settings_display;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_WIFI:
+ name = context.getResources().getString(R.string.power_wifi);
+ iconId = R.drawable.ic_settings_wireless;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_IDLE:
+ case SystemBatteryConsumer.DRAIN_TYPE_MEMORY:
+ name = context.getResources().getString(R.string.power_idle);
+ iconId = R.drawable.ic_settings_phone_idle;
+ break;
+ case SystemBatteryConsumer.DRAIN_TYPE_CUSTOM:
+ name = null;
+ iconId = R.drawable.ic_power_system;
+ break;
+ }
+ } else if (batteryConsumer instanceof UserBatteryConsumer) {
+ UserInfo info = um.getUserInfo(((UserBatteryConsumer) batteryConsumer).getUserId());
+ if (info != null) {
+ icon = Utils.getUserIcon(context, um, info);
+ name = Utils.getUserLabel(context, info);
+ } else {
+ icon = null;
+ name = context.getResources().getString(
+ R.string.running_process_item_removed_user_label);
+ }
}
- switch (sipper.drainType) {
- case IDLE:
- name = context.getResources().getString(R.string.power_idle);
- iconId = R.drawable.ic_settings_phone_idle;
- break;
- case CELL:
- name = context.getResources().getString(R.string.power_cell);
- iconId = R.drawable.ic_cellular_1_bar;
- break;
- case PHONE:
- name = context.getResources().getString(R.string.power_phone);
- iconId = R.drawable.ic_settings_voice_calls;
- break;
- case WIFI:
- name = context.getResources().getString(R.string.power_wifi);
- iconId = R.drawable.ic_settings_wireless;
- break;
- case BLUETOOTH:
- name = context.getResources().getString(R.string.power_bluetooth);
- iconId = com.android.internal.R.drawable.ic_settings_bluetooth;
- break;
- case SCREEN:
- name = context.getResources().getString(R.string.power_screen);
- iconId = R.drawable.ic_settings_display;
- break;
- case FLASHLIGHT:
- name = context.getResources().getString(R.string.power_flashlight);
- iconId = R.drawable.ic_settings_display;
- break;
- case APP:
- PackageManager pm = context.getPackageManager();
- sipper.mPackages = pm.getPackagesForUid(sipper.uidObj.getUid());
- // Apps should only have one package
- if (sipper.mPackages == null || sipper.mPackages.length != 1) {
- name = sipper.packageWithHighestDrain;
- } else {
- defaultPackageName = pm.getPackagesForUid(sipper.uidObj.getUid())[0];
- try {
- ApplicationInfo appInfo =
- pm.getApplicationInfo(defaultPackageName, 0 /* no flags */);
- name = pm.getApplicationLabel(appInfo).toString();
- } catch (NameNotFoundException e) {
- Log.d(TAG, "PackageManager failed to retrieve ApplicationInfo for: "
- + defaultPackageName);
- name = defaultPackageName;
- }
- }
- break;
- case USER: {
- UserInfo info = um.getUserInfo(sipper.userId);
- if (info != null) {
- icon = Utils.getUserIcon(context, um, info);
- name = Utils.getUserLabel(context, info);
- } else {
- icon = null;
- name = context.getResources().getString(
- R.string.running_process_item_removed_user_label);
- }
- } break;
- case UNACCOUNTED:
- name = context.getResources().getString(R.string.power_unaccounted);
- iconId = R.drawable.ic_android;
- break;
- case OVERCOUNTED:
- name = context.getResources().getString(R.string.power_overcounted);
- iconId = R.drawable.ic_android;
- break;
- case CAMERA:
- name = context.getResources().getString(R.string.power_camera);
- iconId = R.drawable.ic_settings_camera;
- break;
- case AMBIENT_DISPLAY:
- name = context.getResources().getString(R.string.ambient_display_screen_title);
- iconId = R.drawable.ic_settings_aod;
- break;
- }
- if (iconId > 0) {
+ if (iconId != 0) {
icon = context.getDrawable(iconId);
}
- if ((name == null || iconId == 0) && this.sipper.uidObj != null) {
- getQuickNameIconForUid(this.sipper.uidObj.getUid());
- }
}
public Drawable getIcon() {
@@ -266,7 +253,7 @@
return name;
}
- void getQuickNameIconForUid(final int uid) {
+ void getQuickNameIconForUid(final int uid, final String[] packages) {
// Locale sync to system config in Settings
final Locale locale = Locale.getDefault();
if (sCurrentLocale != locale) {
@@ -277,28 +264,29 @@
final String uidString = Integer.toString(uid);
if (sUidCache.containsKey(uidString)) {
UidToDetail utd = sUidCache.get(uidString);
- defaultPackageName = utd.packageName;
+ mDefaultPackageName = utd.packageName;
name = utd.name;
icon = utd.icon;
return;
}
- PackageManager pm = context.getPackageManager();
- icon = pm.getDefaultActivityIcon();
- if (pm.getPackagesForUid(uid) == null) {
+
+ if (packages == null || packages.length == 0) {
if (uid == 0) {
- name = context.getResources().getString(R.string.process_kernel_label);
+ name = mContext.getResources().getString(R.string.process_kernel_label);
} else if ("mediaserver".equals(name)) {
- name = context.getResources().getString(R.string.process_mediaserver_label);
+ name = mContext.getResources().getString(R.string.process_mediaserver_label);
} else if ("dex2oat".equals(name)) {
- name = context.getResources().getString(R.string.process_dex2oat_label);
+ name = mContext.getResources().getString(R.string.process_dex2oat_label);
}
iconId = R.drawable.ic_power_system;
- icon = context.getDrawable(iconId);
+ icon = mContext.getDrawable(iconId);
+ } else {
+ icon = mContext.getPackageManager().getDefaultActivityIcon();
}
if (sHandler != null) {
- synchronized (mRequestQueue) {
- mRequestQueue.add(this);
+ synchronized (sRequestQueue) {
+ sRequestQueue.add(this);
}
}
}
@@ -308,17 +296,19 @@
*/
public void loadNameAndIcon() {
// Bail out if the current sipper is not an App sipper.
- if (sipper.uidObj == null) {
+ final int uid = getUid();
+ if (uid == 0 || uid == Process.INVALID_UID) {
return;
}
- PackageManager pm = context.getPackageManager();
- final int uid = sipper.uidObj.getUid();
- if (sipper.mPackages == null) {
- sipper.mPackages = pm.getPackagesForUid(uid);
+ final PackageManager pm = mContext.getPackageManager();
+ final String[] packages;
+ if (uid == Process.SYSTEM_UID) {
+ packages = new String[]{PACKAGE_SYSTEM};
+ } else {
+ packages = pm.getPackagesForUid(uid);
}
- final String[] packages = extractPackagesFromSipper(sipper);
if (packages != null) {
String[] packageLabels = new String[packages.length];
System.arraycopy(packages, 0, packageLabels, 0, packages.length);
@@ -340,7 +330,7 @@
packageLabels[i] = label.toString();
}
if (ai.icon != 0) {
- defaultPackageName = packages[i];
+ mDefaultPackageName = packages[i];
icon = ai.loadIcon(pm);
break;
}
@@ -368,7 +358,7 @@
if (nm != null) {
name = nm.toString();
if (pi.applicationInfo.icon != 0) {
- defaultPackageName = pkgName;
+ mDefaultPackageName = pkgName;
icon = pi.applicationInfo.loadIcon(pm);
}
break;
@@ -394,17 +384,113 @@
UidToDetail utd = new UidToDetail();
utd.name = name;
utd.icon = icon;
- utd.packageName = defaultPackageName;
+ utd.packageName = mDefaultPackageName;
+
sUidCache.put(uidString, utd);
if (sHandler != null) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_UPDATE_NAME_ICON, this));
}
}
- String[] extractPackagesFromSipper(BatterySipper sipper) {
- // Only use system package if uid is system uid, so it could find a consistent name and icon
- return sipper.getUid() == Process.SYSTEM_UID
- ? new String[]{PACKAGE_SYSTEM}
- : sipper.mPackages;
+ /**
+ * Returns a string that uniquely identifies this battery consumer.
+ */
+ public String getKey() {
+ if (mBatteryConsumer instanceof UidBatteryConsumer) {
+ return Integer.toString(((UidBatteryConsumer) mBatteryConsumer).getUid());
+ } else if (mBatteryConsumer instanceof SystemBatteryConsumer) {
+ return "S|" + ((SystemBatteryConsumer) mBatteryConsumer).getDrainType();
+ } else if (mBatteryConsumer instanceof UserBatteryConsumer) {
+ return "U|" + ((UserBatteryConsumer) mBatteryConsumer).getUserId();
+ } else {
+ Log.w(TAG, "Unsupported BatteryConsumer: " + mBatteryConsumer);
+ return "";
+ }
+ }
+
+ /**
+ * Returns true if the entry is hidden from the battery usage summary list.
+ */
+ public boolean isHidden() {
+ return mIsHidden;
+ }
+
+ /**
+ * Returns true if this entry describes an app (UID)
+ */
+ public boolean isAppEntry() {
+ return mBatteryConsumer instanceof UidBatteryConsumer;
+ }
+
+ /**
+ * Returns true if this entry describes a User.
+ */
+ public boolean isUserEntry() {
+ if (mBatteryConsumer instanceof UserBatteryConsumer) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the package name that should be used to represent the UID described
+ * by this entry.
+ */
+ public String getDefaultPackageName() {
+ return mDefaultPackageName;
+ }
+
+ /**
+ * Returns the UID of the app described by this entry.
+ */
+ public int getUid() {
+ if (mBatteryConsumer instanceof UidBatteryConsumer) {
+ return ((UidBatteryConsumer) mBatteryConsumer).getUid();
+ } else {
+ return Process.INVALID_UID;
+ }
+ }
+
+ /**
+ * Returns foreground foreground time (in milliseconds) that is attributed to this entry.
+ */
+ public long getTimeInForegroundMs() {
+ if (mBatteryConsumer instanceof UidBatteryConsumer) {
+ return ((UidBatteryConsumer) mBatteryConsumer).getTimeInStateMs(
+ UidBatteryConsumer.STATE_FOREGROUND);
+ } else {
+ return mBatteryConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE);
+ }
+ }
+
+ /**
+ * Returns background activity time (in milliseconds) that is attributed to this entry.
+ */
+ public long getTimeInBackgroundMs() {
+ if (mBatteryConsumer instanceof UidBatteryConsumer) {
+ return ((UidBatteryConsumer) mBatteryConsumer).getTimeInStateMs(
+ UidBatteryConsumer.STATE_BACKGROUND);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Returns total amount of power (in milli-amp-hours) that is attributed to this entry.
+ */
+ public double getConsumedPower() {
+ return mConsumedPower;
+ }
+
+ /**
+ * Adds the consumed power of the supplied BatteryConsumer to this entry. Also
+ * uses its package with highest drain, if necessary.
+ */
+ public void add(BatteryConsumer batteryConsumer) {
+ mConsumedPower += batteryConsumer.getConsumedPower();
+ if (mDefaultPackageName == null && batteryConsumer instanceof UidBatteryConsumer) {
+ mDefaultPackageName =
+ ((UidBatteryConsumer) batteryConsumer).getPackageWithHighestDrain();
+ }
}
}
diff --git a/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java b/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java
deleted file mode 100644
index 5de83d3..0000000
--- a/src/com/android/settings/fuelgauge/BatteryStatsHelperLoader.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.content.Context;
-import android.os.UserManager;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.internal.os.BatteryStatsHelper;
-import com.android.settingslib.utils.AsyncLoaderCompat;
-
-/**
- * Loader to get new {@link BatteryStatsHelper} in the background
- */
-public class BatteryStatsHelperLoader extends AsyncLoaderCompat<BatteryStatsHelper> {
- @VisibleForTesting
- UserManager mUserManager;
- @VisibleForTesting
- BatteryUtils mBatteryUtils;
-
- public BatteryStatsHelperLoader(Context context) {
- super(context);
- mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
- mBatteryUtils = BatteryUtils.getInstance(context);
- }
-
- @Override
- public BatteryStatsHelper loadInBackground() {
- Context context = getContext();
- final BatteryStatsHelper statsHelper = new BatteryStatsHelper(context,
- true /* collectBatteryBroadcast */);
- mBatteryUtils.initBatteryStatsHelper(statsHelper, null /* bundle */, mUserManager);
-
- return statsHelper;
- }
-
- @Override
- protected void onDiscardResult(BatteryStatsHelper result) {
-
- }
-
-}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 8c0ab46..1645590 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -28,22 +28,18 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Build;
-import android.os.Bundle;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
import android.os.SystemClock;
+import android.os.UidBatteryConsumer;
import android.os.UserHandle;
-import android.os.UserManager;
-import android.text.format.DateUtils;
import android.util.Log;
-import android.util.SparseLongArray;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.util.ArrayUtils;
import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
@@ -61,8 +57,6 @@
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.time.Instant;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
/**
@@ -87,9 +81,8 @@
private static final String TAG = "BatteryUtils";
- private static final int MIN_POWER_THRESHOLD_MILLI_AMP = 5;
+ private static final double MIN_POWER_THRESHOLD_MILLI_AMP_HOURS = 0.002;
- private static final int SECONDS_IN_HOUR = 60 * 60;
private static BatteryUtils sInstance;
private PackageManager mPackageManager;
@@ -174,111 +167,61 @@
}
/**
- * Remove the {@link BatterySipper} that we should hide and smear the screen usage based on
- * foreground activity time.
- *
- * @param sippers sipper list that need to check and remove
- * @return the total power of the hidden items of {@link BatterySipper}
- * for proportional smearing
+ * Returns true if the specified battery consumer should be excluded from the summary
+ * battery consumption list.
*/
- public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
- double proportionalSmearPowerMah = 0;
- BatterySipper screenSipper = null;
- for (int i = sippers.size() - 1; i >= 0; i--) {
- final BatterySipper sipper = sippers.get(i);
- if (shouldHideSipper(sipper)) {
- sippers.remove(i);
- if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
- && sipper.drainType != BatterySipper.DrainType.SCREEN
- && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
- && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
- && sipper.drainType != BatterySipper.DrainType.WIFI
- && sipper.drainType != BatterySipper.DrainType.IDLE
- && !isHiddenSystemModule(sipper)) {
- // Don't add it if it is overcounted, unaccounted, wifi, bluetooth, screen
- // or hidden system modules
- proportionalSmearPowerMah += sipper.totalPowerMah;
- }
- }
-
- if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
- screenSipper = sipper;
- }
- }
-
- smearScreenBatterySipper(sippers, screenSipper);
-
- return proportionalSmearPowerMah;
+ public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer) {
+ return shouldHideUidBatteryConsumer(consumer,
+ mPackageManager.getPackagesForUid(consumer.getUid()));
}
/**
- * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
- * time.
+ * Returns true if the specified battery consumer should be excluded from the summary
+ * battery consumption list.
*/
- @VisibleForTesting
- void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
- long totalActivityTimeMs = 0;
- final SparseLongArray activityTimeArray = new SparseLongArray();
- for (int i = 0, size = sippers.size(); i < size; i++) {
- final BatteryStats.Uid uid = sippers.get(i).uidObj;
- if (uid != null) {
- final long timeMs = getProcessTimeMs(StatusType.SCREEN_USAGE, uid,
- BatteryStats.STATS_SINCE_CHARGED);
- activityTimeArray.put(uid.getUid(), timeMs);
- totalActivityTimeMs += timeMs;
- }
- }
+ public boolean shouldHideUidBatteryConsumer(UidBatteryConsumer consumer, String[] packages) {
+ return consumer.getConsumedPower() < MIN_POWER_THRESHOLD_MILLI_AMP_HOURS
+ || mPowerUsageFeatureProvider.isTypeSystem(consumer.getUid(), packages)
+ || shouldHideUidBatteryConsumerUnconditionally(consumer, packages);
+ }
- if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
- if (screenSipper == null) {
- Log.e(TAG, "screen sipper is null even when app screen time is not zero");
- return;
- }
+ /**
+ * Returns true if the specified battery consumer should be excluded from
+ * battery consumption lists, either short or full.
+ */
+ boolean shouldHideUidBatteryConsumerUnconditionally(UidBatteryConsumer consumer,
+ String[] packages) {
+ return consumer.getUid() < 0 || isHiddenSystemModule(packages);
+ }
- final double screenPowerMah = screenSipper.totalPowerMah;
- for (int i = 0, size = sippers.size(); i < size; i++) {
- final BatterySipper sipper = sippers.get(i);
- sipper.totalPowerMah += screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
- / totalActivityTimeMs;
- }
+ /**
+ * Returns true if the specified battery consumer should be excluded from the summary
+ * battery consumption list.
+ */
+ public boolean shouldHideSystemBatteryConsumer(SystemBatteryConsumer consumer) {
+ switch (consumer.getDrainType()) {
+ case SystemBatteryConsumer.DRAIN_TYPE_IDLE:
+ case SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO:
+ case SystemBatteryConsumer.DRAIN_TYPE_SCREEN:
+ case SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH:
+ case SystemBatteryConsumer.DRAIN_TYPE_WIFI:
+ return true;
+ default:
+ return consumer.getConsumedPower() < MIN_POWER_THRESHOLD_MILLI_AMP_HOURS;
}
}
/**
- * Check whether we should hide the battery sipper.
+ * Returns true if one the specified packages belongs to a hidden system module.
*/
- public boolean shouldHideSipper(BatterySipper sipper) {
- final BatterySipper.DrainType drainType = sipper.drainType;
-
- return drainType == BatterySipper.DrainType.IDLE
- || drainType == BatterySipper.DrainType.CELL
- || drainType == BatterySipper.DrainType.SCREEN
- || drainType == BatterySipper.DrainType.UNACCOUNTED
- || drainType == BatterySipper.DrainType.OVERCOUNTED
- || drainType == BatterySipper.DrainType.BLUETOOTH
- || drainType == BatterySipper.DrainType.WIFI
- || (sipper.totalPowerMah * SECONDS_IN_HOUR) < MIN_POWER_THRESHOLD_MILLI_AMP
- || mPowerUsageFeatureProvider.isTypeService(sipper)
- || mPowerUsageFeatureProvider.isTypeSystem(sipper)
- || isHiddenSystemModule(sipper);
- }
-
- /**
- * Return {@code true} if one of packages in {@code sipper} is hidden system modules
- */
- public boolean isHiddenSystemModule(BatterySipper sipper) {
- if (sipper.uidObj == null) {
- return false;
- }
- sipper.mPackages = mPackageManager.getPackagesForUid(sipper.getUid());
- if (sipper.mPackages != null) {
- for (int i = 0, length = sipper.mPackages.length; i < length; i++) {
- if (AppUtils.isHiddenSystemModule(mContext, sipper.mPackages[i])) {
+ public boolean isHiddenSystemModule(String[] packages) {
+ if (packages != null) {
+ for (int i = 0, length = packages.length; i < length; i++) {
+ if (AppUtils.isHiddenSystemModule(mContext, packages[i])) {
return true;
}
}
}
-
return false;
}
@@ -287,36 +230,17 @@
*
* @param powerUsageMah power used by the app
* @param totalPowerMah total power used in the system
- * @param hiddenPowerMah power used by no-actionable app that we want to hide, i.e. Screen,
- * Android OS.
* @param dischargeAmount The discharge amount calculated by {@link BatteryStats}
* @return A percentage value scaled by {@paramref dischargeAmount}
* @see BatteryStats#getDischargeAmount(int)
*/
public double calculateBatteryPercent(double powerUsageMah, double totalPowerMah,
- double hiddenPowerMah, int dischargeAmount) {
+ int dischargeAmount) {
if (totalPowerMah == 0) {
return 0;
}
- return (powerUsageMah / (totalPowerMah - hiddenPowerMah)) * dischargeAmount;
- }
-
- /**
- * Calculate the whole running time in the state {@code statsType}
- *
- * @param batteryStatsHelper utility class that contains the data
- * @param statsType state that we want to calculate the time for
- * @return the running time in millis
- */
- public long calculateRunningTimeBasedOnStatsType(BatteryStatsHelper batteryStatsHelper,
- int statsType) {
- final long elapsedRealtimeUs = PowerUtil.convertMsToUs(
- SystemClock.elapsedRealtime());
- // Return the battery time (millisecond) on status mStatsType
- return PowerUtil.convertUsToMs(
- batteryStatsHelper.getStats().computeBatteryRealtime(elapsedRealtimeUs, statsType));
-
+ return (powerUsageMah / totalPowerMah) * dischargeAmount;
}
/**
@@ -366,40 +290,15 @@
}
/**
- * Sort the {@code usageList} based on {@link BatterySipper#totalPowerMah}
- */
- public void sortUsageList(List<BatterySipper> usageList) {
- Collections.sort(usageList, new Comparator<BatterySipper>() {
- @Override
- public int compare(BatterySipper a, BatterySipper b) {
- return Double.compare(b.totalPowerMah, a.totalPowerMah);
- }
- });
- }
-
- /**
* Calculate the time since last full charge, including the device off time
*
- * @param batteryStatsHelper utility class that contains the data
+ * @param batteryUsageStats class that contains the data
* @param currentTimeMs current wall time
* @return time in millis
*/
- public long calculateLastFullChargeTime(BatteryStatsHelper batteryStatsHelper,
+ public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats,
long currentTimeMs) {
- return currentTimeMs - batteryStatsHelper.getStats().getStartClockTime();
-
- }
-
- /**
- * Calculate the screen usage time since last full charge.
- *
- * @param batteryStatsHelper utility class that contains the screen usage data
- * @return time in millis
- */
- public long calculateScreenUsageTime(BatteryStatsHelper batteryStatsHelper) {
- final BatterySipper sipper = findBatterySipperByType(
- batteryStatsHelper.getUsageList(), BatterySipper.DrainType.SCREEN);
- return sipper != null ? sipper.usageTimeMs : 0;
+ return currentTimeMs - batteryUsageStats.getStatsStartRealtime();
}
public static void logRuntime(String tag, String message, long startTime) {
@@ -467,13 +366,6 @@
}
}
- public void initBatteryStatsHelper(BatteryStatsHelper statsHelper, Bundle bundle,
- UserManager userManager) {
- statsHelper.create(bundle);
- statsHelper.clearStats();
- statsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, userManager.getUserProfiles());
- }
-
@WorkerThread
public BatteryInfo getBatteryInfo(final String tag) {
final BatteryStatsManager systemService = mContext.getSystemService(
@@ -526,20 +418,6 @@
return estimate;
}
- /**
- * Find the {@link BatterySipper} with the corresponding {@link BatterySipper.DrainType}
- */
- public BatterySipper findBatterySipperByType(List<BatterySipper> usageList,
- BatterySipper.DrainType type) {
- for (int i = 0, size = usageList.size(); i < size; i++) {
- final BatterySipper sipper = usageList.get(i);
- if (sipper.drainType == type) {
- return sipper;
- }
- }
- return null;
- }
-
private boolean isDataCorrupted() {
return mPackageManager == null || mAppOpsManager == null;
}
@@ -674,4 +552,3 @@
return -1L;
}
}
-
diff --git a/src/com/android/settings/fuelgauge/FakeUid.java b/src/com/android/settings/fuelgauge/FakeUid.java
index 4bb98ed..b49fb10 100644
--- a/src/com/android/settings/fuelgauge/FakeUid.java
+++ b/src/com/android/settings/fuelgauge/FakeUid.java
@@ -356,7 +356,7 @@
}
@Override
- public long getScreenOnMeasuredBatteryConsumptionUC() {
+ public long getBluetoothMeasuredBatteryConsumptionUC() {
return 0;
}
@@ -366,6 +366,16 @@
}
@Override
+ public long getScreenOnMeasuredBatteryConsumptionUC() {
+ return 0;
+ }
+
+ @Override
+ public long getWifiMeasuredBatteryConsumptionUC() {
+ return 0;
+ }
+
+ @Override
public long[] getCustomConsumerMeasuredBatteryConsumptionUC() {
return null;
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
index 86e52d9..9279e5d 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageAdvanced.java
@@ -50,7 +50,6 @@
@VisibleForTesting
BatteryHistoryPreference mHistPref;
- private BatteryUtils mBatteryUtils;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
@VisibleForTesting
@@ -64,7 +63,6 @@
mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context);
- mBatteryUtils = BatteryUtils.getInstance(context);
// init the summary so other preferences won't have unnecessary move
updateHistPrefSummary(context);
@@ -155,7 +153,7 @@
updatePreference(mHistPref);
updateHistPrefSummary(context);
- mBatteryAppListPreferenceController.refreshAppListGroup(mStatsHelper, mShowAllApps);
+ mBatteryAppListPreferenceController.refreshAppListGroup(mBatteryUsageStats, mShowAllApps);
}
private void updateHistPrefSummary(Context context) {
diff --git a/src/com/android/settings/fuelgauge/PowerUsageBase.java b/src/com/android/settings/fuelgauge/PowerUsageBase.java
index 29ecedc..28d7715 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageBase.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageBase.java
@@ -29,7 +29,6 @@
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.dashboard.DashboardFragment;
/**
@@ -44,10 +43,8 @@
private static final String KEY_REFRESH_TYPE = "refresh_type";
private static final String KEY_INCLUDE_HISTORY = "include_history";
- private static final int LOADER_BATTERY_STATS_HELPER = 0;
private static final int LOADER_BATTERY_USAGE_STATS = 1;
- protected BatteryStatsHelper mStatsHelper;
@VisibleForTesting
BatteryUsageStats mBatteryUsageStats;
@@ -55,12 +52,6 @@
private BatteryBroadcastReceiver mBatteryBroadcastReceiver;
protected boolean mIsBatteryPresent = true;
- // TODO(b/180630447): switch to BatteryUsageStatsLoader and remove all references to
- // BatteryStatsHelper and BatterySipper
- @VisibleForTesting
- final BatteryStatsHelperLoaderCallbacks mBatteryStatsHelperLoaderCallbacks =
- new BatteryStatsHelperLoaderCallbacks();
-
@VisibleForTesting
final BatteryUsageStatsLoaderCallbacks mBatteryUsageStatsLoaderCallbacks =
new BatteryUsageStatsLoaderCallbacks();
@@ -69,13 +60,11 @@
public void onAttach(Activity activity) {
super.onAttach(activity);
mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE);
- mStatsHelper = new BatteryStatsHelper(activity, true);
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- mStatsHelper.create(icicle);
setHasOptionsMenu(true);
mBatteryBroadcastReceiver = new BatteryBroadcastReceiver(getContext());
@@ -103,18 +92,11 @@
final Bundle bundle = new Bundle();
bundle.putInt(KEY_REFRESH_TYPE, refreshType);
bundle.putBoolean(KEY_INCLUDE_HISTORY, isBatteryHistoryNeeded());
- getLoaderManager().restartLoader(LOADER_BATTERY_STATS_HELPER, bundle,
- mBatteryStatsHelperLoaderCallbacks);
getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle,
mBatteryUsageStatsLoaderCallbacks);
}
private void onLoadFinished(@BatteryUpdateType int refreshType) {
- // Wait for both loaders to finish before proceeding.
- if (mStatsHelper == null || mBatteryUsageStats == null) {
- return;
- }
-
refreshUi(refreshType);
}
@@ -127,28 +109,6 @@
BatteryUtils.logRuntime(TAG, "updatePreference", startTime);
}
- private class BatteryStatsHelperLoaderCallbacks
- implements LoaderManager.LoaderCallbacks<BatteryStatsHelper> {
- private int mRefreshType;
-
- @Override
- public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
- mRefreshType = args.getInt(KEY_REFRESH_TYPE);
- return new BatteryStatsHelperLoader(getContext());
- }
-
- @Override
- public void onLoadFinished(Loader<BatteryStatsHelper> loader,
- BatteryStatsHelper batteryHelper) {
- mStatsHelper = batteryHelper;
- PowerUsageBase.this.onLoadFinished(mRefreshType);
- }
-
- @Override
- public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
- }
- }
-
private class BatteryUsageStatsLoaderCallbacks
implements LoaderManager.LoaderCallbacks<BatteryUsageStats> {
private int mRefreshType;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 4f292dd..6a22ed4 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -59,6 +59,11 @@
boolean isTypeSystem(BatterySipper sipper);
/**
+ * Check whether it is type system
+ */
+ boolean isTypeSystem(int uid, String[] packages);
+
+ /**
* Check whether the toggle for power accounting is enabled
*/
boolean isPowerAccountingToggleEnabled();
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index ab71c97..cb83d80 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -66,6 +66,21 @@
}
@Override
+ public boolean isTypeSystem(int uid, String[] packages) {
+ // Classify all the sippers to type system if the range of uid is 0...FIRST_APPLICATION_UID
+ if (uid >= Process.ROOT_UID && uid < Process.FIRST_APPLICATION_UID) {
+ return true;
+ } else if (packages != null) {
+ for (final String packageName : packages) {
+ if (ArrayUtils.contains(PACKAGES_SYSTEM, packageName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean isLocationSettingEnabled(String[] packages) {
return false;
}
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index 4f8ac62..9e61997 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -108,7 +108,7 @@
@Override
public Loader<List<BatteryTip>> onCreateLoader(int id, Bundle args) {
- return new BatteryTipLoader(getContext(), mStatsHelper);
+ return new BatteryTipLoader(getContext(), mBatteryUsageStats);
}
@Override
diff --git a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java
index c06a4ff..f75fccc 100644
--- a/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java
+++ b/src/com/android/settings/fuelgauge/RequestIgnoreBatteryOptimizations.java
@@ -94,6 +94,13 @@
}
@Override
+ protected void onStart() {
+ super.onStart();
+ getWindow().addSystemFlags(android.view.WindowManager.LayoutParams
+ .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ }
+
+ @Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case BUTTON_POSITIVE:
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index 9b80a1f..433c06d 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -17,10 +17,10 @@
package com.android.settings.fuelgauge.batterytip;
import android.content.Context;
+import android.os.BatteryUsageStats;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.detectors.BatteryDefenderDetector;
@@ -48,13 +48,13 @@
private static final boolean USE_FAKE_DATA = false;
- private BatteryStatsHelper mBatteryStatsHelper;
+ private BatteryUsageStats mBatteryUsageStats;
@VisibleForTesting
BatteryUtils mBatteryUtils;
- public BatteryTipLoader(Context context, BatteryStatsHelper batteryStatsHelper) {
+ public BatteryTipLoader(Context context, BatteryUsageStats batteryUsageStats) {
super(context);
- mBatteryStatsHelper = batteryStatsHelper;
+ mBatteryUsageStats = batteryUsageStats;
mBatteryUtils = BatteryUtils.getInstance(context);
}
@@ -69,7 +69,7 @@
final Context context = getContext();
tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect());
- tips.add(new HighUsageDetector(context, policy, mBatteryStatsHelper, batteryInfo).detect());
+ tips.add(new HighUsageDetector(context, policy, mBatteryUsageStats, batteryInfo).detect());
tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
tips.add(new EarlyWarningDetector(policy, context).detect());
tips.add(new BatteryDefenderDetector(batteryInfo).detect());
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
index 928ae52..4b3f2df 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetector.java
@@ -19,12 +19,11 @@
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import android.content.Context;
-import android.os.BatteryStats;
+import android.os.BatteryUsageStats;
+import android.os.UidBatteryConsumer;
import androidx.annotation.VisibleForTesting;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.AppInfo;
@@ -34,7 +33,6 @@
import com.android.settings.fuelgauge.batterytip.tips.HighUsageTip;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -44,7 +42,7 @@
*/
public class HighUsageDetector implements BatteryTipDetector {
private BatteryTipPolicy mPolicy;
- private BatteryStatsHelper mBatteryStatsHelper;
+ private BatteryUsageStats mBatteryUsageStats;
private final BatteryInfo mBatteryInfo;
private List<AppInfo> mHighUsageAppList;
@VisibleForTesting
@@ -55,9 +53,9 @@
boolean mDischarging;
public HighUsageDetector(Context context, BatteryTipPolicy policy,
- BatteryStatsHelper batteryStatsHelper, BatteryInfo batteryInfo) {
+ BatteryUsageStats batteryUsageStats, BatteryInfo batteryInfo) {
mPolicy = policy;
- mBatteryStatsHelper = batteryStatsHelper;
+ mBatteryUsageStats = batteryUsageStats;
mBatteryInfo = batteryInfo;
mHighUsageAppList = new ArrayList<>();
mBatteryUtils = BatteryUtils.getInstance(context);
@@ -69,37 +67,35 @@
@Override
public BatteryTip detect() {
final long lastFullChargeTimeMs = mBatteryUtils.calculateLastFullChargeTime(
- mBatteryStatsHelper, System.currentTimeMillis());
+ mBatteryUsageStats, System.currentTimeMillis());
if (mPolicy.highUsageEnabled && mDischarging) {
parseBatteryData();
if (mDataParser.isDeviceHeavilyUsed() || mPolicy.testHighUsageTip) {
- final BatteryStats batteryStats = mBatteryStatsHelper.getStats();
- final List<BatterySipper> batterySippers
- = new ArrayList<>(mBatteryStatsHelper.getUsageList());
- final double totalPower = mBatteryStatsHelper.getTotalPower();
- final int dischargeAmount = batteryStats != null
- ? batteryStats.getDischargeAmount(BatteryStats.STATS_SINCE_CHARGED)
- : 0;
-
- Collections.sort(batterySippers,
- (sipper1, sipper2) -> Double.compare(sipper2.totalSmearedPowerMah,
- sipper1.totalSmearedPowerMah));
- for (BatterySipper batterySipper : batterySippers) {
+ final double totalPower = mBatteryUsageStats.getConsumedPower();
+ final int dischargeAmount = mBatteryUsageStats.getDischargePercentage();
+ final List<UidBatteryConsumer> uidBatteryConsumers =
+ mBatteryUsageStats.getUidBatteryConsumers();
+ // Sort by descending power
+ uidBatteryConsumers.sort(
+ (consumer1, consumer2) -> Double.compare(consumer2.getConsumedPower(),
+ consumer1.getConsumedPower()));
+ for (UidBatteryConsumer consumer : uidBatteryConsumers) {
final double percent = mBatteryUtils.calculateBatteryPercent(
- batterySipper.totalSmearedPowerMah, totalPower, 0, dischargeAmount);
- if ((percent + 0.5f < 1f) || mBatteryUtils.shouldHideSipper(batterySipper)) {
+ consumer.getConsumedPower(), totalPower, dischargeAmount);
+ if ((percent + 0.5f < 1f)
+ || mBatteryUtils.shouldHideUidBatteryConsumer(consumer)) {
// Don't show it if we should hide or usage percentage is lower than 1%
continue;
}
+
mHighUsageAppList.add(new AppInfo.Builder()
- .setUid(batterySipper.getUid())
+ .setUid(consumer.getUid())
.setPackageName(
- mBatteryUtils.getPackageName(batterySipper.getUid()))
+ mBatteryUtils.getPackageName(consumer.getUid()))
.build());
if (mHighUsageAppList.size() >= mPolicy.highUsageAppCount) {
break;
}
-
}
// When in test mode, add an app if necessary
diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
index 14fe6a6..388d87a 100644
--- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
+++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
@@ -30,15 +30,12 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.SettingsTutorialDialogWrapperActivity;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
@@ -188,12 +185,7 @@
protected boolean setDefaultKey(String key) {
setCurrentSystemNavigationMode(mOverlayManager, key);
setIllustrationVideo(mVideoPreference, key);
- if (TextUtils.equals(KEY_SYSTEM_NAV_GESTURAL, key) && (
- isAnyServiceSupportAccessibilityButton() || isNavBarMagnificationEnabled())) {
- Intent intent = new Intent(getActivity(), SettingsTutorialDialogWrapperActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
+
return true;
}
@@ -267,18 +259,6 @@
}
}
- private boolean isAnyServiceSupportAccessibilityButton() {
- final AccessibilityManager ams = getContext().getSystemService(AccessibilityManager.class);
- final List<String> targets = ams.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- return !targets.isEmpty();
- }
-
- private boolean isNavBarMagnificationEnabled() {
- return Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
- }
-
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.system_navigation_gesture_settings) {
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/AirplaneModeConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/AirplaneModeConditionController.java
index 2f55b3f..8bd3028 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/AirplaneModeConditionController.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/AirplaneModeConditionController.java
@@ -40,11 +40,13 @@
private final ConditionManager mConditionManager;
private final Context mAppContext;
private final Receiver mReceiver;
+ private final ConnectivityManager mConnectivityManager;
public AirplaneModeConditionController(Context appContext, ConditionManager conditionManager) {
mAppContext = appContext;
mConditionManager = conditionManager;
mReceiver = new Receiver();
+ mConnectivityManager = mAppContext.getSystemService(ConnectivityManager.class);
}
@Override
@@ -65,7 +67,7 @@
@Override
public void onActionClick() {
- ConnectivityManager.from(mAppContext).setAirplaneMode(false);
+ mConnectivityManager.setAirplaneMode(false);
}
@Override
diff --git a/src/com/android/settings/homepage/contextualcards/conditional/CellularDataConditionController.java b/src/com/android/settings/homepage/contextualcards/conditional/CellularDataConditionController.java
index 4c0ddc9..9c936b9 100644
--- a/src/com/android/settings/homepage/contextualcards/conditional/CellularDataConditionController.java
+++ b/src/com/android/settings/homepage/contextualcards/conditional/CellularDataConditionController.java
@@ -19,7 +19,6 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
import android.telephony.PhoneStateListener;
import android.telephony.PreciseDataConnectionState;
import android.telephony.SubscriptionManager;
@@ -39,7 +38,6 @@
private final Context mAppContext;
private final ConditionManager mConditionManager;
private final GlobalSettingsChangeListener mDefaultDataSubscriptionIdListener;
- private final ConnectivityManager mConnectivityManager;
private int mSubId;
private TelephonyManager mTelephonyManager;
@@ -63,8 +61,6 @@
}
}
};
- mConnectivityManager = appContext.getSystemService(
- ConnectivityManager.class);
}
@Override
@@ -74,7 +70,7 @@
@Override
public boolean isDisplayable() {
- if (!mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)
+ if (!mTelephonyManager.isDataCapable()
|| mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
return false;
}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
index 6a5b300..94d3f69 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
@@ -29,6 +29,7 @@
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.BatteryUsageStats;
import android.util.ArrayMap;
import android.view.View;
@@ -40,11 +41,10 @@
import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
-import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
+import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController;
@@ -206,9 +206,10 @@
@WorkerThread
@VisibleForTesting
static List<BatteryTip> refreshBatteryTips(Context context) {
- final BatteryStatsHelperLoader statsLoader = new BatteryStatsHelperLoader(context);
- final BatteryStatsHelper statsHelper = statsLoader.loadInBackground();
- final BatteryTipLoader loader = new BatteryTipLoader(context, statsHelper);
+ final BatteryUsageStatsLoader statsLoader = new BatteryUsageStatsLoader(context,
+ /* includeBatteryHistory */ false);
+ final BatteryUsageStats batteryUsageStats = statsLoader.loadInBackground();
+ final BatteryTipLoader loader = new BatteryTipLoader(context, batteryUsageStats);
final List<BatteryTip> batteryTips = loader.loadInBackground();
for (BatteryTip batteryTip : batteryTips) {
if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
diff --git a/src/com/android/settings/location/LocationPersonalSettings.java b/src/com/android/settings/location/LocationPersonalSettings.java
index 38b7c4a..bdf2d2b 100644
--- a/src/com/android/settings/location/LocationPersonalSettings.java
+++ b/src/com/android/settings/location/LocationPersonalSettings.java
@@ -53,6 +53,7 @@
// STOPSHIP(b/180533061): resolve the personal/work location services issue before we can
// ship.
use(LocationFooterPreferenceController.class).init(this);
+ use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
final RecentLocationAccessPreferenceController controller = use(
diff --git a/src/com/android/settings/location/LocationServicesPreferenceController.java b/src/com/android/settings/location/LocationServicesPreferenceController.java
index f7a3388..53150a8 100644
--- a/src/com/android/settings/location/LocationServicesPreferenceController.java
+++ b/src/com/android/settings/location/LocationServicesPreferenceController.java
@@ -17,8 +17,6 @@
package com.android.settings.location;
import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.provider.Settings;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -28,29 +26,8 @@
*/
public class LocationServicesPreferenceController extends BasePreferenceController {
- private final WifiManager mWifiManager;
-
public LocationServicesPreferenceController(Context context, String key) {
super(context, key);
- mWifiManager = context.getSystemService(WifiManager.class);
- }
-
- @Override
- public CharSequence getSummary() {
- final boolean wifiScanOn = mWifiManager.isScanAlwaysAvailable();
- final boolean bleScanOn = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
- int resId;
- if (wifiScanOn && bleScanOn) {
- resId = R.string.scanning_status_text_wifi_on_ble_on;
- } else if (wifiScanOn && !bleScanOn) {
- resId = R.string.scanning_status_text_wifi_on_ble_off;
- } else if (!wifiScanOn && bleScanOn) {
- resId = R.string.scanning_status_text_wifi_off_ble_on;
- } else {
- resId = R.string.scanning_status_text_wifi_off_ble_off;
- }
- return mContext.getString(resId);
}
@AvailabilityStatus
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index d58ad5b..bb971bf 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -83,6 +83,7 @@
use(AppLocationPermissionPreferenceController.class).init(this);
use(RecentLocationAccessPreferenceController.class).init(this);
+ use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
use(LocationFooterPreferenceController.class).init(this);
use(LocationForWorkPreferenceController.class).init(this);
use(LocationInjectedServicesForWorkPreferenceController.class).init(this);
diff --git a/src/com/android/settings/location/LocationWorkProfileSettings.java b/src/com/android/settings/location/LocationWorkProfileSettings.java
index 18936fd..6783075 100644
--- a/src/com/android/settings/location/LocationWorkProfileSettings.java
+++ b/src/com/android/settings/location/LocationWorkProfileSettings.java
@@ -52,6 +52,7 @@
use(AppLocationPermissionPreferenceController.class).init(this);
use(LocationFooterPreferenceController.class).init(this);
use(LocationForWorkPreferenceController.class).init(this);
+ use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
final RecentLocationAccessPreferenceController controller = use(
diff --git a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
index 383fbea..97de4a7 100644
--- a/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessPreferenceController.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.Intent;
+import android.icu.text.RelativeDateTimeFormatter;
import android.os.UserHandle;
import android.os.UserManager;
@@ -29,6 +30,7 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settingslib.location.RecentLocationAccesses;
+import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.AppPreference;
import java.util.ArrayList;
@@ -113,7 +115,8 @@
@Override
public void onLocationModeChanged(int mode, boolean restricted) {
- mCategoryRecentLocationRequests.setEnabled(mLocationEnabler.isEnabled(mode));
+ boolean enabled = mLocationEnabler.isEnabled(mode);
+ mCategoryRecentLocationRequests.setVisible(enabled);
}
/**
@@ -133,6 +136,9 @@
final AppPreference pref = new AppPreference(prefContext);
pref.setIcon(access.icon);
pref.setTitle(access.label);
+ pref.setSummary(StringUtil.formatRelativeTime(prefContext,
+ System.currentTimeMillis() - access.accessFinishTime, false,
+ RelativeDateTimeFormatter.Style.SHORT));
pref.setOnPreferenceClickListener(new PackageEntryClickedListener(
fragment.getContext(), access.packageName, access.userHandle));
return pref;
diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllButtonPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessSeeAllButtonPreferenceController.java
new file mode 100644
index 0000000..68cde63
--- /dev/null
+++ b/src/com/android/settings/location/RecentLocationAccessSeeAllButtonPreferenceController.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021 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.location;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+/**
+ * Preference controller that handles the "See All" button for recent location access.
+ */
+public class RecentLocationAccessSeeAllButtonPreferenceController extends
+ LocationBasePreferenceController {
+
+ private Preference mPreference;
+
+ /**
+ * Constructor of {@link RecentLocationAccessSeeAllButtonPreferenceController}.
+ */
+ public RecentLocationAccessSeeAllButtonPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onLocationModeChanged(int mode, boolean restricted) {
+ boolean enabled = mLocationEnabler.isEnabled(mode);
+ mPreference.setVisible(enabled);
+ }
+}
diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
index c147ee7..a05092d 100644
--- a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
@@ -37,7 +37,7 @@
extends LocationBasePreferenceController {
private PreferenceScreen mCategoryAllRecentLocationAccess;
- private RecentLocationAccesses mRecentLocationAccesses;
+ private final RecentLocationAccesses mRecentLocationAccesses;
private boolean mShowSystem = false;
private Preference mPreference;
private int mType = ProfileSelectFragment.ProfileType.ALL;
diff --git a/src/com/android/settings/network/AllowedNetworkTypesListener.java b/src/com/android/settings/network/AllowedNetworkTypesListener.java
index ecc1d9c..972e4d6 100644
--- a/src/com/android/settings/network/AllowedNetworkTypesListener.java
+++ b/src/com/android/settings/network/AllowedNetworkTypesListener.java
@@ -17,7 +17,6 @@
package com.android.settings.network;
import android.content.Context;
-import android.telephony.PhoneStateListener;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -26,7 +25,6 @@
import java.util.concurrent.Executor;
-
/**
* {@link TelephonyCallback} to listen to Allowed Network Types changed
*/
@@ -49,7 +47,7 @@
}
/**
- * Register a PhoneStateListener for Allowed Network Types changed.
+ * Register a TelephonyCallback for Allowed Network Types changed.
* @param context the Context
* @param subId the subscription id.
*/
@@ -60,7 +58,7 @@
}
/**
- * Unregister a PhoneStateListener for Allowed Network Types changed.
+ * Unregister a TelephonyCallback for Allowed Network Types changed.
* @param context the Context
* @param subId the subscription id.
*/
diff --git a/src/com/android/settings/network/EraseEuiccDataController.java b/src/com/android/settings/network/EraseEuiccDataController.java
index d221fbd..7d71096 100644
--- a/src/com/android/settings/network/EraseEuiccDataController.java
+++ b/src/com/android/settings/network/EraseEuiccDataController.java
@@ -28,8 +28,7 @@
/**
* Controller for erasing Euicc data
*/
-public class EraseEuiccDataController extends BasePreferenceController implements
- PreferenceControllerMixin {
+public class EraseEuiccDataController extends BasePreferenceController {
private ResetDashboardFragment mHostFragment;
public EraseEuiccDataController(Context context, String preferenceKey) {
diff --git a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
index 3aee9b1..a141f0c 100644
--- a/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
+++ b/src/com/android/settings/network/EraseEuiccDataDialogFragment.java
@@ -67,7 +67,7 @@
@Override
public void onClick(DialogInterface dialog, int which) {
- Fragment fragment = getTargetFragment();
+ final Fragment fragment = getTargetFragment();
if (!(fragment instanceof ResetDashboardFragment)) {
Log.e(TAG, "getTargetFragment return unexpected type");
}
diff --git a/src/com/android/settings/network/InternetPreferenceController.java b/src/com/android/settings/network/InternetPreferenceController.java
index f64ed30..a6c8574 100644
--- a/src/com/android/settings/network/InternetPreferenceController.java
+++ b/src/com/android/settings/network/InternetPreferenceController.java
@@ -65,7 +65,7 @@
@VisibleForTesting
static Map<Integer, Integer> sIconMap = new HashMap<>();
static {
- sIconMap.put(INTERNET_OFF, R.drawable.ic_no_internet_unavailable);
+ sIconMap.put(INTERNET_OFF, R.drawable.ic_no_internet_airplane);
sIconMap.put(INTERNET_NETWORKS_AVAILABLE, R.drawable.ic_no_internet_available);
sIconMap.put(INTERNET_WIFI, R.drawable.ic_wifi_signal_4);
sIconMap.put(INTERNET_CELLULAR, R.drawable.ic_network_cell);
diff --git a/src/com/android/settings/network/MobileNetworkPreferenceController.java b/src/com/android/settings/network/MobileNetworkPreferenceController.java
index b968438..527a632 100644
--- a/src/com/android/settings/network/MobileNetworkPreferenceController.java
+++ b/src/com/android/settings/network/MobileNetworkPreferenceController.java
@@ -28,6 +28,7 @@
import android.provider.Settings;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import androidx.annotation.VisibleForTesting;
@@ -55,7 +56,7 @@
private final UserManager mUserManager;
private Preference mPreference;
@VisibleForTesting
- PhoneStateListener mPhoneStateListener;
+ MobileNetworkTelephonyCallback mTelephonyCallback;
private BroadcastReceiver mAirplanModeChangedReceiver;
@@ -97,18 +98,22 @@
return KEY_MOBILE_NETWORK_SETTINGS;
}
+ class MobileNetworkTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.ServiceStateListener {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ updateState(mPreference);
+ }
+ }
+
@OnLifecycleEvent(Event.ON_START)
public void onStart() {
if (isAvailable()) {
- if (mPhoneStateListener == null) {
- mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onServiceStateChanged(ServiceState serviceState) {
- updateState(mPreference);
- }
- };
+ if (mTelephonyCallback == null) {
+ mTelephonyCallback = new MobileNetworkTelephonyCallback();
}
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ mTelephonyManager.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallback);
}
if (mAirplanModeChangedReceiver != null) {
mContext.registerReceiver(mAirplanModeChangedReceiver,
@@ -118,8 +123,8 @@
@OnLifecycleEvent(Event.ON_STOP)
public void onStop() {
- if (mPhoneStateListener != null) {
- mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ if (mTelephonyCallback != null) {
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
}
if (mAirplanModeChangedReceiver != null) {
mContext.unregisterReceiver(mAirplanModeChangedReceiver);
diff --git a/src/com/android/settings/network/MobilePlanPreferenceController.java b/src/com/android/settings/network/MobilePlanPreferenceController.java
index b4135b8..d8963ad 100644
--- a/src/com/android/settings/network/MobilePlanPreferenceController.java
+++ b/src/com/android/settings/network/MobilePlanPreferenceController.java
@@ -47,7 +47,6 @@
import java.util.List;
-
public class MobilePlanPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnSaveInstanceState {
diff --git a/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java b/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java
index 7239b00..4b452c0 100644
--- a/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java
+++ b/src/com/android/settings/network/NetworkScorerPickerPreferenceController.java
@@ -23,7 +23,6 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.core.AbstractPreferenceController;
import java.util.List;
diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
index ea29a1d..822aad0 100644
--- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
+++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
@@ -160,7 +160,7 @@
final Context context = getContext();
final ContentResolver contentResolver = context.getContentResolver();
- mMode = ConnectivityManager.getPrivateDnsMode(contentResolver);
+ mMode = ConnectivityManager.getPrivateDnsMode(context);
mEditText = view.findViewById(R.id.private_dns_mode_provider_hostname);
mEditText.addTextChangedListener(this);
diff --git a/src/com/android/settings/network/PrivateDnsPreferenceController.java b/src/com/android/settings/network/PrivateDnsPreferenceController.java
index 3136040..4aa92f4 100644
--- a/src/com/android/settings/network/PrivateDnsPreferenceController.java
+++ b/src/com/android/settings/network/PrivateDnsPreferenceController.java
@@ -118,7 +118,7 @@
public CharSequence getSummary() {
final Resources res = mContext.getResources();
final ContentResolver cr = mContext.getContentResolver();
- final String mode = ConnectivityManager.getPrivateDnsMode(cr);
+ final String mode = ConnectivityManager.getPrivateDnsMode(mContext);
final LinkProperties lp = mLatestLinkProperties;
final List<InetAddress> dnses = (lp == null) ? null : lp.getValidatedPrivateDnsServers();
final boolean dnsesResolved = !ArrayUtils.isEmpty(dnses);
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
index a1fdb1c..18765a8 100644
--- a/src/com/android/settings/network/ProviderModelSlice.java
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -147,31 +147,6 @@
listBuilder.addRow(getWifiSliceItemRow(item));
}
}
-
- // Fifth section: If device has connection problem, this row show the message for user.
- // 1) show non_carrier_network_unavailable:
- // - while no wifi item
- // 2) show all_network_unavailable:
- // - while no wifi item + no carrier
- // - while no wifi item + no data capability
- if (worker == null || wifiList == null || wifiList.size() == 0) {
- log("no wifi item");
- int resId = R.string.non_carrier_network_unavailable;
- if (!hasCarrier || !mHelper.isDataSimActive()) {
- log("No carrier item or no carrier data.");
- resId = R.string.all_network_unavailable;
- }
-
- if (!hasCarrier && !hasEthernet) {
- // If there is no item in ProviderModelItem, slice needs a header.
- listBuilder.setHeader(mHelper.createHeader(
- NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS));
- }
- listBuilder.addGridRow(
- mHelper.createMessageGridRow(resId,
- NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS));
- }
-
return listBuilder.build();
}
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index 8ae4197..440d425 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -36,7 +36,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
-import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
@@ -79,12 +78,6 @@
Log.d(TAG, s);
}
- protected ListBuilder.HeaderBuilder createHeader(String intentAction) {
- return new ListBuilder.HeaderBuilder()
- .setTitle(mContext.getText(R.string.summary_placeholder))
- .setPrimaryAction(getPrimarySliceAction(intentAction));
- }
-
protected ListBuilder createListBuilder(Uri uri) {
final ListBuilder builder = new ListBuilder(mContext, uri, ListBuilder.INFINITY)
.setAccentColor(-1)
@@ -92,14 +85,6 @@
return builder;
}
- protected GridRowBuilder createMessageGridRow(int messageResId, String intentAction) {
- final CharSequence title = mContext.getText(messageResId);
- return new GridRowBuilder()
- // Add cells to the grid row.
- .addCell(new GridRowBuilder.CellBuilder().addTitleText(title))
- .setPrimaryAction(getPrimarySliceAction(intentAction));
- }
-
@Nullable
protected WifiSliceItem getConnectedWifiItem(List<WifiSliceItem> wifiList) {
if (wifiList == null) {
@@ -111,7 +96,10 @@
return item.isPresent() ? item.get() : null;
}
- protected boolean hasCarrier() {
+ /**
+ * @return whether there is the carrier item in the slice.
+ */
+ public boolean hasCarrier() {
if (isAirplaneModeEnabled()
|| mSubscriptionManager == null || mTelephonyManager == null
|| mSubscriptionManager.getDefaultDataSubscriptionId()
@@ -175,7 +163,12 @@
return mTelephonyManager.isDataEnabled();
}
- protected boolean isDataSimActive() {
+ /**
+ * To check the carrier data status.
+ *
+ * @return whether the carrier data is active.
+ */
+ public boolean isDataSimActive() {
return isNoCarrierData() ? false : MobileNetworkUtils.activeNetworkIsCellular(mContext);
}
@@ -193,11 +186,6 @@
return mobileDataOnAndNoData || mobileDataOffAndOutOfService;
}
- private boolean isAirplaneSafeNetworksModeEnabled() {
- // TODO: isAirplaneSafeNetworksModeEnabled is not READY
- return false;
- }
-
@VisibleForTesting
Drawable getMobileDrawable(Drawable drawable) throws Throwable {
// set color and drawable
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index 9295414..e815d49 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -47,7 +47,6 @@
import java.util.List;
-
public class VpnPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnResume, OnPause {
diff --git a/src/com/android/settings/network/ims/ImsQueryEnhanced4gLteModeUserSetting.java b/src/com/android/settings/network/ims/ImsQueryEnhanced4gLteModeUserSetting.java
index 34d8430..c6c5ad3 100644
--- a/src/com/android/settings/network/ims/ImsQueryEnhanced4gLteModeUserSetting.java
+++ b/src/com/android/settings/network/ims/ImsQueryEnhanced4gLteModeUserSetting.java
@@ -19,7 +19,6 @@
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
-
/**
* An {@link ImsQuery} for accessing IMS user setting for enhanced 4G LTE
*/
diff --git a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
index b52d22c..44c4519 100644
--- a/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
+++ b/src/com/android/settings/network/ims/ImsQueryProvisioningStat.java
@@ -21,7 +21,6 @@
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.util.Log;
-
/**
* An {@link ImsQuery} for accessing IMS provision stat
*/
diff --git a/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
index e2719dd..8a306c5 100644
--- a/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
+++ b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
@@ -19,7 +19,6 @@
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
-
/**
* An {@link ImsQuery} for accessing IMS tty on VoLte stat
*/
diff --git a/src/com/android/settings/network/ims/ImsQueryVtUserSetting.java b/src/com/android/settings/network/ims/ImsQueryVtUserSetting.java
index 6da4a4c..91cecb1 100644
--- a/src/com/android/settings/network/ims/ImsQueryVtUserSetting.java
+++ b/src/com/android/settings/network/ims/ImsQueryVtUserSetting.java
@@ -19,7 +19,6 @@
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
-
/**
* An {@link ImsQuery} for accessing IMS VT enabled settings from user
*/
diff --git a/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
index 3407413..5e4d471 100644
--- a/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
+++ b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
@@ -19,7 +19,6 @@
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
-
/**
* An {@link ImsQuery} for accessing IMS WFC enabled settings from user
*/
diff --git a/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java b/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java
index 26398d7..e43865b 100644
--- a/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java
+++ b/src/com/android/settings/network/telephony/ContactDiscoveryDialogFragment.java
@@ -22,11 +22,9 @@
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.ims.ImsManager;
-import android.telephony.ims.ImsRcsManager;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
-import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
diff --git a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
index b231da2..a2a20bb 100644
--- a/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
+++ b/src/com/android/settings/network/telephony/ContactDiscoveryPreferenceController.java
@@ -37,7 +37,6 @@
import com.android.settings.network.SubscriptionUtil;
-
/**
* Controller for the "Contact Discovery" option present in MobileNetworkSettings.
*/
diff --git a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
index c2b1fc4..5ea7c20 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
@@ -20,11 +20,11 @@
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
@@ -34,6 +34,7 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
+import com.android.internal.telephony.util.ArrayUtils;
import com.android.settings.R;
import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -53,7 +54,7 @@
@VisibleForTesting
Preference mPreference;
- private PhoneCallStateListener mPhoneStateListener;
+ private PhoneCallStateTelephonyCallback mTelephonyCallback;
private boolean mShow5gLimitedDialog;
boolean mIsNrEnabledFromCarrierConfig;
private boolean mHas5gCapability;
@@ -72,8 +73,8 @@
}
public Enhanced4gBasePreferenceController init(int subId) {
- if (mPhoneStateListener == null) {
- mPhoneStateListener = new PhoneCallStateListener();
+ if (mTelephonyCallback == null) {
+ mTelephonyCallback = new PhoneCallStateTelephonyCallback();
}
if (mSubId == subId) {
@@ -95,9 +96,10 @@
mShow5gLimitedDialog = carrierConfig.getBoolean(
CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL);
- mIsNrEnabledFromCarrierConfig = carrierConfig.getInt(
- CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITY_INT)
- != CarrierConfigManager.CARRIER_NR_AVAILABILITY_NONE;
+
+ int[] nrAvailabilities = carrierConfig.getIntArray(
+ CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY);
+ mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities);
return this;
}
@@ -134,18 +136,18 @@
@Override
public void onStart() {
- if (!isModeMatched() || (mPhoneStateListener == null)) {
+ if (!isModeMatched() || (mTelephonyCallback == null)) {
return;
}
- mPhoneStateListener.register(mContext, mSubId);
+ mTelephonyCallback.register(mContext, mSubId);
}
@Override
public void onStop() {
- if (mPhoneStateListener == null) {
+ if (mTelephonyCallback == null) {
return;
}
- mPhoneStateListener.unregister();
+ mTelephonyCallback.unregister();
}
@Override
@@ -218,16 +220,13 @@
CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL);
}
- private class PhoneCallStateListener extends PhoneStateListener {
-
- PhoneCallStateListener() {
- super(Looper.getMainLooper());
- }
+ private class PhoneCallStateTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
private TelephonyManager mTelephonyManager;
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onCallStateChanged(int state) {
mCallState = state;
updateState(mPreference);
}
@@ -240,7 +239,8 @@
// assign current call state so that it helps to show correct preference state even
// before first onCallStateChanged() by initial registration.
mCallState = mTelephonyManager.getCallState(subId);
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
+ mTelephonyManager.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallback);
final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily();
mHas5gCapability =
@@ -250,7 +250,7 @@
public void unregister() {
mCallState = null;
if (mTelephonyManager != null) {
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.unregisterTelephonyCallback(this);
}
}
}
diff --git a/src/com/android/settings/network/telephony/MobileDataDialogFragment.java b/src/com/android/settings/network/telephony/MobileDataDialogFragment.java
index 9533827..0292c6b 100644
--- a/src/com/android/settings/network/telephony/MobileDataDialogFragment.java
+++ b/src/com/android/settings/network/telephony/MobileDataDialogFragment.java
@@ -31,7 +31,6 @@
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.wifi.WifiPickerTrackerHelper;
-
/**
* Dialog Fragment to show dialog for "mobile data"
*
diff --git a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
index 78dfe51..fd5df6f 100644
--- a/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
+++ b/src/com/android/settings/network/telephony/NrDisabledInDsdsFooterPreferenceController.java
@@ -28,7 +28,6 @@
import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.HelpUtils;
-
/**
* Class to show the footer that can't connect to 5G when device is in DSDS mode.
*/
diff --git a/src/com/android/settings/network/telephony/SignalStrengthListener.java b/src/com/android/settings/network/telephony/SignalStrengthListener.java
index 0e29a45..8d7304d 100644
--- a/src/com/android/settings/network/telephony/SignalStrengthListener.java
+++ b/src/com/android/settings/network/telephony/SignalStrengthListener.java
@@ -19,9 +19,12 @@
import android.content.Context;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
+import androidx.annotation.VisibleForTesting;
+
import com.google.common.collect.Sets;
import java.util.Map;
@@ -34,7 +37,9 @@
private TelephonyManager mBaseTelephonyManager;
private Callback mCallback;
- private Map<Integer, PhoneStateListener> mListeners;
+ private Context mContext;
+ @VisibleForTesting
+ Map<Integer, SignalStrengthTelephonyCallback> mTelephonyCallbacks;
public interface Callback {
void onSignalStrengthChanged();
@@ -43,20 +48,21 @@
public SignalStrengthListener(Context context, Callback callback) {
mBaseTelephonyManager = context.getSystemService(TelephonyManager.class);
mCallback = callback;
- mListeners = new TreeMap<>();
+ mContext = context;
+ mTelephonyCallbacks = new TreeMap<>();
}
/** Resumes listening for signal strength changes for the set of ids from the last call to
* {@link #updateSubscriptionIds(Set)} */
public void resume() {
- for (int subId : mListeners.keySet()) {
+ for (int subId : mTelephonyCallbacks.keySet()) {
startListening(subId);
}
}
/** Pauses listening for signal strength changes */
public void pause() {
- for (int subId : mListeners.keySet()) {
+ for (int subId : mTelephonyCallbacks.keySet()) {
stopListening(subId);
}
}
@@ -64,30 +70,36 @@
/** Updates the set of ids we want to be listening for, beginning to listen for any new ids and
* stopping listening for any ids not contained in the new set */
public void updateSubscriptionIds(Set<Integer> ids) {
- Set<Integer> currentIds = new ArraySet<>(mListeners.keySet());
+ Set<Integer> currentIds = new ArraySet<>(mTelephonyCallbacks.keySet());
for (int idToRemove : Sets.difference(currentIds, ids)) {
stopListening(idToRemove);
- mListeners.remove(idToRemove);
+ mTelephonyCallbacks.remove(idToRemove);
}
for (int idToAdd : Sets.difference(ids, currentIds)) {
- PhoneStateListener listener = new PhoneStateListener() {
- @Override
- public void onSignalStrengthsChanged(SignalStrength signalStrength) {
- mCallback.onSignalStrengthChanged();
- }
- };
- mListeners.put(idToAdd, listener);
+ SignalStrengthTelephonyCallback telephonyCallback =
+ new SignalStrengthTelephonyCallback();
+ mTelephonyCallbacks.put(idToAdd, telephonyCallback);
startListening(idToAdd);
}
}
+ @VisibleForTesting
+ class SignalStrengthTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.SignalStrengthsListener {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mCallback.onSignalStrengthChanged();
+ }
+ }
+
private void startListening(int subId) {
TelephonyManager mgr = mBaseTelephonyManager.createForSubscriptionId(subId);
- mgr.listen(mListeners.get(subId), PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+ mgr.registerTelephonyCallback(
+ mContext.getMainExecutor(), mTelephonyCallbacks.get(subId));
}
private void stopListening(int subId) {
TelephonyManager mgr = mBaseTelephonyManager.createForSubscriptionId(subId);
- mgr.listen(mListeners.get(subId), PhoneStateListener.LISTEN_NONE);
+ mgr.unregisterTelephonyCallback(mTelephonyCallbacks.get(subId));
}
}
diff --git a/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
index c1acd91..557f2c4 100644
--- a/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
+++ b/src/com/android/settings/network/telephony/TelephonyAvailabilityHandler.java
@@ -20,8 +20,6 @@
*/
package com.android.settings.network.telephony;
-import android.content.Context;
-
public interface TelephonyAvailabilityHandler {
/**
diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
index fa8b47f..b3421f4 100644
--- a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
@@ -17,11 +17,11 @@
package com.android.settings.network.telephony;
import android.content.Context;
-import android.os.Looper;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
@@ -50,7 +50,7 @@
private Preference mPreference;
private CarrierConfigManager mCarrierConfigManager;
- private PhoneCallStateListener mPhoneStateListener;
+ private PhoneTelephonyCallback mTelephonyCallback;
@VisibleForTesting
Integer mCallState;
private MobileDataEnabledListener mDataContentObserver;
@@ -59,7 +59,7 @@
super(context, key);
mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
mDataContentObserver = new MobileDataEnabledListener(context, this);
- mPhoneStateListener = new PhoneCallStateListener();
+ mTelephonyCallback = new PhoneTelephonyCallback();
}
@Override
@@ -78,13 +78,13 @@
@Override
public void onStart() {
- mPhoneStateListener.register(mContext, mSubId);
+ mTelephonyCallback.register(mContext, mSubId);
mDataContentObserver.start(mSubId);
}
@Override
public void onStop() {
- mPhoneStateListener.unregister();
+ mTelephonyCallback.unregister();
mDataContentObserver.stop();
}
@@ -163,16 +163,13 @@
updateState(mPreference);
}
- private class PhoneCallStateListener extends PhoneStateListener {
-
- PhoneCallStateListener() {
- super(Looper.getMainLooper());
- }
+ private class PhoneTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
private TelephonyManager mTelephonyManager;
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onCallStateChanged(int state) {
mCallState = state;
updateState(mPreference);
}
@@ -185,12 +182,12 @@
// assign current call state so that it helps to show correct preference state even
// before first onCallStateChanged() by initial registration.
mCallState = mTelephonyManager.getCallState(subId);
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
+ mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(), this);
}
public void unregister() {
mCallState = null;
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.unregisterTelephonyCallback(this);
}
}
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
index 94a5999..2d7ba38 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
@@ -20,7 +20,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.Looper;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
@@ -28,6 +27,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.Log;
@@ -60,13 +60,13 @@
private ImsMmTelManager mImsMmTelManager;
@VisibleForTesting
PhoneAccountHandle mSimCallManager;
- private PhoneCallStateListener mPhoneStateListener;
+ private PhoneTelephonyCallback mTelephonyCallback;
private Preference mPreference;
public WifiCallingPreferenceController(Context context, String key) {
super(context, key);
mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
- mPhoneStateListener = new PhoneCallStateListener();
+ mTelephonyCallback = new PhoneTelephonyCallback();
}
@Override
@@ -79,12 +79,12 @@
@Override
public void onStart() {
- mPhoneStateListener.register(mContext, mSubId);
+ mTelephonyCallback.register(mContext, mSubId);
}
@Override
public void onStop() {
- mPhoneStateListener.unregister();
+ mTelephonyCallback.unregister();
}
@Override
@@ -195,16 +195,13 @@
}
- private class PhoneCallStateListener extends PhoneStateListener {
-
- PhoneCallStateListener() {
- super(Looper.getMainLooper());
- }
+ private class PhoneTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
private TelephonyManager mTelephonyManager;
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onCallStateChanged(int state) {
mCallState = state;
updateState(mPreference);
}
@@ -214,12 +211,12 @@
// assign current call state so that it helps to show correct preference state even
// before first onCallStateChanged() by initial registration.
mCallState = mTelephonyManager.getCallState(subId);
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
+ mTelephonyManager.registerTelephonyCallback(context.getMainExecutor(), this);
}
public void unregister() {
mCallState = null;
- mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
+ mTelephonyManager.unregisterTelephonyCallback(this);
}
}
}
diff --git a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
index cdeb1e5..4047009 100644
--- a/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
+++ b/src/com/android/settings/network/telephony/gsm/OpenNetworkSelectPagePreferenceController.java
@@ -41,7 +41,6 @@
import com.android.settings.network.telephony.NetworkSelectSettings;
import com.android.settings.network.telephony.TelephonyBasePreferenceController;
-
/**
* Preference controller for "Open network select"
*/
diff --git a/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
index bab0d5f..5396be5 100644
--- a/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
+++ b/src/com/android/settings/notification/RecentNotifyingAppsPreferenceController.java
@@ -46,10 +46,10 @@
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.app.AppNotificationSettings;
import com.android.settings.widget.PrimarySwitchPreference;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.utils.StringUtil;
+import com.android.settingslib.widget.TwoTargetPreference;
import java.util.ArrayList;
import java.util.Calendar;
diff --git a/src/com/android/settings/notification/app/RecentConversationPreference.java b/src/com/android/settings/notification/app/RecentConversationPreference.java
index 49e2c02..167fdd6 100644
--- a/src/com/android/settings/notification/app/RecentConversationPreference.java
+++ b/src/com/android/settings/notification/app/RecentConversationPreference.java
@@ -21,7 +21,7 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
-import com.android.settingslib.TwoTargetPreference;
+import com.android.settingslib.widget.TwoTargetPreference;
import com.google.common.annotations.VisibleForTesting;
diff --git a/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java
index a431883..8bfcd13 100644
--- a/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java
+++ b/src/com/android/settings/notification/zen/ZenModeButtonPreferenceController.java
@@ -25,6 +25,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.SettingsEnableZenModeDialog;
@@ -90,9 +91,11 @@
case Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
mPreference.updateStatus(true);
+ mPreference.setTitle(R.string.do_not_disturb_main_switch_title_on);
break;
case Settings.Global.ZEN_MODE_OFF:
default:
+ mPreference.setTitle(R.string.do_not_disturb_main_switch_title_off);
mPreference.updateStatus(false);
}
}
diff --git a/src/com/android/settings/notification/zen/ZenRulePreference.java b/src/com/android/settings/notification/zen/ZenRulePreference.java
index 1f1283d..b8c8354 100644
--- a/src/com/android/settings/notification/zen/ZenRulePreference.java
+++ b/src/com/android/settings/notification/zen/ZenRulePreference.java
@@ -33,8 +33,8 @@
import com.android.settings.R;
import com.android.settings.utils.ManagedServiceSettings;
import com.android.settings.utils.ZenServiceListing;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.widget.TwoTargetPreference;
import java.util.Map;
diff --git a/src/com/android/settings/panel/InternetConnectivityPanel.java b/src/com/android/settings/panel/InternetConnectivityPanel.java
index 4fda0a4..238cbb4 100644
--- a/src/com/android/settings/panel/InternetConnectivityPanel.java
+++ b/src/com/android/settings/panel/InternetConnectivityPanel.java
@@ -22,10 +22,23 @@
import static com.android.settings.network.NetworkProviderSettings.ACTION_NETWORK_PROVIDER_SETTINGS;
import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.net.Uri;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Looper;
import android.provider.Settings;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LifecycleObserver;
@@ -35,6 +48,9 @@
import com.android.settings.Utils;
import com.android.settings.network.AirplaneModePreferenceController;
import com.android.settings.network.InternetUpdater;
+import com.android.settings.network.ProviderModelSliceHelper;
+import com.android.settings.network.SubscriptionsChangeListener;
+import com.android.settings.network.telephony.DataConnectivityListener;
import com.android.settings.slices.CustomSliceRegistry;
import java.util.ArrayList;
@@ -44,23 +60,69 @@
* Represents the Internet Connectivity Panel.
*/
public class InternetConnectivityPanel implements PanelContent, LifecycleObserver,
- InternetUpdater.InternetChangeListener {
+ InternetUpdater.InternetChangeListener, DataConnectivityListener.Client,
+ SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
+ private static final String TAG = "InternetConnectivityPanel";
+ private static final int SUBTITLE_TEXT_NONE = -1;
+ private static final int SUBTITLE_TEXT_WIFI_IS_TURNED_ON = R.string.wifi_is_turned_on_subtitle;
+ private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.non_carrier_network_unavailable;
+ private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
+ R.string.all_network_unavailable;
private final Context mContext;
+ private final WifiManager mWifiManager;
+ private final IntentFilter mWifiStateFilter;
+ private final NetworkProviderTelephonyCallback mTelephonyCallback;
+ private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ if (TextUtils.equals(intent.getAction(), WifiManager.NETWORK_STATE_CHANGED_ACTION)
+ || TextUtils.equals(intent.getAction(),
+ WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ updatePanelTitle();
+ }
+ }
+ };
+
@VisibleForTesting
boolean mIsProviderModelEnabled;
- private PanelContentCallback mCallback;
@VisibleForTesting
InternetUpdater mInternetUpdater;
+ @VisibleForTesting
+ ProviderModelSliceHelper mProviderModelSliceHelper;
- public static InternetConnectivityPanel create(Context context) {
- return new InternetConnectivityPanel(context);
- }
+ private int mSubtitle = SUBTITLE_TEXT_NONE;
+ private PanelContentCallback mCallback;
+ private TelephonyManager mTelephonyManager;
+ private SubscriptionsChangeListener mSubscriptionsListener;
+ private DataConnectivityListener mConnectivityListener;
+ private int mDefaultDataSubid = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private InternetConnectivityPanel(Context context) {
mContext = context.getApplicationContext();
mIsProviderModelEnabled = Utils.isProviderModelEnabled(mContext);
mInternetUpdater = new InternetUpdater(context, null /* Lifecycle */, this);
+
+ mSubscriptionsListener = new SubscriptionsChangeListener(context, this);
+ mConnectivityListener = new DataConnectivityListener(context, this);
+ mTelephonyCallback = new NetworkProviderTelephonyCallback();
+ mDefaultDataSubid = getDefaultDataSubscriptionId();
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+
+ mWifiManager = mContext.getSystemService(WifiManager.class);
+ mWifiStateFilter = new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mWifiStateFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+ mProviderModelSliceHelper = new ProviderModelSliceHelper(mContext, null);
+ }
+
+ /** create the panel */
+ public static InternetConnectivityPanel create(Context context) {
+ return new InternetConnectivityPanel(context);
}
/** @OnLifecycleEvent(ON_RESUME) */
@@ -70,6 +132,12 @@
return;
}
mInternetUpdater.onResume();
+ mSubscriptionsListener.start();
+ mConnectivityListener.start();
+ mTelephonyManager.registerTelephonyCallback(
+ new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback);
+ mContext.registerReceiver(mWifiStateReceiver, mWifiStateFilter);
+ updatePanelTitle();
}
/** @OnLifecycleEvent(ON_PAUSE) */
@@ -79,6 +147,10 @@
return;
}
mInternetUpdater.onPause();
+ mSubscriptionsListener.stop();
+ mConnectivityListener.stop();
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
+ mContext.unregisterReceiver(mWifiStateReceiver);
}
/**
@@ -98,9 +170,8 @@
*/
@Override
public CharSequence getSubTitle() {
- if (mIsProviderModelEnabled && mInternetUpdater.isAirplaneModeOn()
- && mInternetUpdater.isWifiEnabled()) {
- return mContext.getText(R.string.wifi_is_turned_on_subtitle);
+ if (mIsProviderModelEnabled && mSubtitle != SUBTITLE_TEXT_NONE) {
+ return mContext.getText(mSubtitle);
}
return null;
}
@@ -170,15 +241,36 @@
updatePanelTitle();
}
- private void updatePanelTitle() {
+ @Override
+ public void onSubscriptionsChanged() {
+ final int defaultDataSubId = getDefaultDataSubscriptionId();
+ log("onSubscriptionsChanged: defaultDataSubId:" + defaultDataSubId);
+ if (mDefaultDataSubid == defaultDataSubId) {
+ return;
+ }
+ if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) {
+ mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback);
+ mTelephonyManager.registerTelephonyCallback(
+ new HandlerExecutor(new Handler(Looper.getMainLooper())), mTelephonyCallback);
+ }
+ updatePanelTitle();
+ }
+
+ @Override
+ public void onDataConnectivityChange() {
+ log("onDataConnectivityChange");
+ updatePanelTitle();
+ }
+
+ @VisibleForTesting
+ void updatePanelTitle() {
if (mCallback == null) {
return;
}
+ updateSubtitleText();
- if (mInternetUpdater.isAirplaneModeOn() && mInternetUpdater.isWifiEnabled()) {
- // When the airplane mode is on and Wi-Fi is enabled.
- // Title: Airplane mode
- // Sub-Title: Wi-Fi is turned on
+ log("Subtitle:" + mSubtitle);
+ if (mSubtitle != SUBTITLE_TEXT_NONE) {
mCallback.onHeaderChanged();
} else {
// Other situations.
@@ -187,4 +279,63 @@
}
mCallback.onCustomizedButtonStateChanged();
}
+
+ @VisibleForTesting
+ int getDefaultDataSubscriptionId() {
+ return SubscriptionManager.getDefaultDataSubscriptionId();
+ }
+
+ private void updateSubtitleText() {
+ mSubtitle = SUBTITLE_TEXT_NONE;
+ if (!mInternetUpdater.isWifiEnabled()) {
+ return;
+ }
+
+ if (mInternetUpdater.isAirplaneModeOn()) {
+ // When the airplane mode is on and Wi-Fi is enabled.
+ // Title: Airplane mode
+ // Sub-Title: Wi-Fi is turned on
+ log("Airplane mode is on + Wi-Fi on.");
+ mSubtitle = SUBTITLE_TEXT_WIFI_IS_TURNED_ON;
+ return;
+ }
+
+ final List<ScanResult> wifiList = mWifiManager.getScanResults();
+ if (wifiList != null && wifiList.size() == 0) {
+ // Sub-Title:
+ // show non_carrier_network_unavailable
+ // - while Wi-Fi on + no Wi-Fi item
+ // show all_network_unavailable:
+ // - while Wi-Fi on + no Wi-Fi item + no carrier
+ // - while Wi-Fi on + no Wi-Fi item + no data capability
+ log("No Wi-Fi item.");
+ mSubtitle = SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE;
+ if (!mProviderModelSliceHelper.hasCarrier()
+ || !mProviderModelSliceHelper.isDataSimActive()) {
+ log("No carrier item or no carrier data.");
+ mSubtitle = SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE;
+ }
+ }
+ }
+
+ private class NetworkProviderTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.DataConnectionStateListener,
+ TelephonyCallback.ServiceStateListener {
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ log("onServiceStateChanged voiceState=" + state.getState()
+ + " dataState=" + state.getDataRegistrationState());
+ updatePanelTitle();
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ log("onDataConnectionStateChanged: networkType=" + networkType + " state=" + state);
+ updatePanelTitle();
+ }
+ }
+
+ private static void log(String s) {
+ Log.d(TAG, s);
+ }
}
diff --git a/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java b/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java
new file mode 100644
index 0000000..4622431
--- /dev/null
+++ b/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Controller for preference to toggle whether clipboard access notifications should be shown.
+ */
+public class ShowClipAccessNotificationPreferenceController extends TogglePreferenceController {
+
+ private static final String KEY_SHOW_CLIP_ACCESS_NOTIFICATION = "show_clip_access_notification";
+
+ public ShowClipAccessNotificationPreferenceController(Context context) {
+ super(context, KEY_SHOW_CLIP_ACCESS_NOTIFICATION);
+ }
+
+ @Override
+ public boolean isChecked() {
+ // TODO(b/182349993) Retrieve default value from DeviceConfig.
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) != 0;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, isChecked ? 1 : 0);
+ return true;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+}
diff --git a/src/com/android/settings/security/ScreenPinningSettings.java b/src/com/android/settings/security/ScreenPinningSettings.java
index a8cb1c9..3fa098b 100644
--- a/src/com/android/settings/security/ScreenPinningSettings.java
+++ b/src/com/android/settings/security/ScreenPinningSettings.java
@@ -215,7 +215,7 @@
private void updateDisplay() {
if (isLockToAppEnabled(getActivity())) {
- mUseScreenLock.setVisible(true);
+ mUseScreenLock.setEnabled(true);
mUseScreenLock.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -226,7 +226,7 @@
mUseScreenLock.setTitle(getCurrentSecurityTitle());
} else {
mFooterPreference.setSummary(getAppPinningContent());
- mUseScreenLock.setVisible(false);
+ mUseScreenLock.setEnabled(false);
}
}
diff --git a/src/com/android/settings/sim/CallsSimListDialogFragment.java b/src/com/android/settings/sim/CallsSimListDialogFragment.java
index 6dd262b..58da111 100644
--- a/src/com/android/settings/sim/CallsSimListDialogFragment.java
+++ b/src/com/android/settings/sim/CallsSimListDialogFragment.java
@@ -18,7 +18,6 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index 42bc67d..03e3613 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -592,7 +592,7 @@
// 0 is a last resort default, but the interface validates that the proxy port is
// present and non-zero.
int port = proxyPort.isEmpty() ? 0 : Integer.parseInt(proxyPort);
- profile.proxy = new ProxyInfo(proxyHost, port, null);
+ profile.proxy = ProxyInfo.buildDirectProxy(proxyHost, port);
} else {
profile.proxy = null;
}
diff --git a/src/com/android/settings/vpn2/OWNERS b/src/com/android/settings/vpn2/OWNERS
index 0419e24..894d604 100644
--- a/src/com/android/settings/vpn2/OWNERS
+++ b/src/com/android/settings/vpn2/OWNERS
@@ -4,6 +4,7 @@
maze@google.com
reminv@google.com
xiaom@google.com
+goldmanj@google.com
# Emergency approvers in case the above are not available
satk@google.com
diff --git a/src/com/android/settings/wfd/WifiDisplaySettings.java b/src/com/android/settings/wfd/WifiDisplaySettings.java
index 21352b7..19b6c54 100755
--- a/src/com/android/settings/wfd/WifiDisplaySettings.java
+++ b/src/com/android/settings/wfd/WifiDisplaySettings.java
@@ -64,9 +64,9 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.TwoTargetPreference;
/**
* The Settings screen for WifiDisplay configuration and connection management.
diff --git a/src/com/android/settings/widget/CheckableRelativeLayout.java b/src/com/android/settings/widget/CheckableRelativeLayout.java
new file mode 100644
index 0000000..d26c042
--- /dev/null
+++ b/src/com/android/settings/widget/CheckableRelativeLayout.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Checkable;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * A RelativeLayout which implements {@link Checkable}. With this implementation, it could be used
+ * in the list item layout for {@link android.widget.AbsListView} to change UI after item click.
+ * Its checked state would be propagated to the checkable child.
+ *
+ * <p>
+ * To support accessibility, the state description is from the checkable view and is
+ * changed with {@link #setChecked(boolean)}. We make the checkable child unclickable, unfocusable
+ * and non-important for accessibility, so that the announcement wouldn't include
+ * the checkable view.
+ * <
+ */
+public class CheckableRelativeLayout extends RelativeLayout implements Checkable {
+
+ private Checkable mCheckable;
+ private View mCheckableChild;
+ private boolean mChecked;
+
+ public CheckableRelativeLayout(Context context) {
+ super(context);
+ }
+
+ public CheckableRelativeLayout(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ mCheckableChild = findFirstCheckableView(this);
+ if (mCheckableChild != null) {
+ mCheckableChild.setClickable(false);
+ mCheckableChild.setFocusable(false);
+ mCheckableChild.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ mCheckable = (Checkable) mCheckableChild;
+ mCheckable.setChecked(isChecked());
+ setStateDescriptionIfNeeded();
+ }
+ super.onFinishInflate();
+ }
+
+ @Nullable
+ private static View findFirstCheckableView(@NonNull ViewGroup viewGroup) {
+ final int childCount = viewGroup.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = viewGroup.getChildAt(i);
+ if (child instanceof Checkable) {
+ return child;
+ }
+ if (child instanceof ViewGroup) {
+ findFirstCheckableView((ViewGroup) child);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ if (mChecked != checked) {
+ mChecked = checked;
+ if (mCheckable != null) {
+ mCheckable.setChecked(checked);
+ }
+ }
+ setStateDescriptionIfNeeded();
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+ }
+
+ private void setStateDescriptionIfNeeded() {
+ if (mCheckableChild == null) {
+ return;
+ }
+ setStateDescription(mCheckableChild.getStateDescription());
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!mChecked);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ event.setChecked(mChecked);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setChecked(mChecked);
+ }
+}
diff --git a/src/com/android/settings/widget/FilterTouchesSwitchPreference.java b/src/com/android/settings/widget/FilterTouchesSwitchPreference.java
index 1b4d681..5721854 100644
--- a/src/com/android/settings/widget/FilterTouchesSwitchPreference.java
+++ b/src/com/android/settings/widget/FilterTouchesSwitchPreference.java
@@ -47,6 +47,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
+ setSingleLineTitle(true);
super.onBindViewHolder(holder);
final View switchView = holder.findViewById(android.R.id.switch_widget);
if (switchView != null) {
diff --git a/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java b/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java
deleted file mode 100644
index e269818..0000000
--- a/src/com/android/settings/widget/LabeledContinuousSeekBarPreference.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 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.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.settings.R;
-
-/** A continuous labeled slider preference */
-public class LabeledContinuousSeekBarPreference extends LabeledSeekBarPreference {
- public LabeledContinuousSeekBarPreference(Context context) {
- this(context, null);
- }
-
- public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs,
- int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public LabeledContinuousSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- setLayoutResource(R.layout.preference_labeled_continuous_slider);
- }
-}
diff --git a/src/com/android/settings/widget/PrimaryCheckBoxPreference.java b/src/com/android/settings/widget/PrimaryCheckBoxPreference.java
index a784d5d..c90f198 100644
--- a/src/com/android/settings/widget/PrimaryCheckBoxPreference.java
+++ b/src/com/android/settings/widget/PrimaryCheckBoxPreference.java
@@ -25,7 +25,7 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
-import com.android.settingslib.TwoTargetPreference;
+import com.android.settingslib.widget.TwoTargetPreference;
/**
* A custom preference that provides inline checkbox. It has a mandatory field for title, and
diff --git a/src/com/android/settings/widget/SettingsMainSwitchBar.java b/src/com/android/settings/widget/SettingsMainSwitchBar.java
index 733be0a..ce2dde5 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchBar.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchBar.java
@@ -84,9 +84,8 @@
}
/**
- * If admin is not null, disables the text and switch but keeps the view clickable.
- * Otherwise, calls setEnabled which will enables the entire view including
- * the text and switch.
+ * If admin is not null, disables the text and switch but keeps the view clickable (unless the
+ * switch is disabled for other reasons). Otherwise, calls setEnabled.
*/
public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
mEnforcedAdmin = admin;
@@ -101,7 +100,7 @@
mDisabledByAdmin = false;
mSwitch.setVisibility(View.VISIBLE);
mRestrictedIcon.setVisibility(View.GONE);
- setEnabled(true);
+ setEnabled(isEnabled());
}
}
diff --git a/src/com/android/settings/widget/SettingsMainSwitchPreference.java b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
index 80a0021..f627e31 100644
--- a/src/com/android/settings/widget/SettingsMainSwitchPreference.java
+++ b/src/com/android/settings/widget/SettingsMainSwitchPreference.java
@@ -186,6 +186,7 @@
* Enable or disable the text and switch.
*/
public void setSwitchBarEnabled(boolean enabled) {
+ setEnabled(enabled);
if (mMainSwitchBar != null) {
mMainSwitchBar.setEnabled(enabled);
}
diff --git a/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java b/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java
index 727c61e..876f5e9 100644
--- a/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java
+++ b/src/com/android/settings/wifi/AppStateChangeWifiStateBridge.java
@@ -27,6 +27,7 @@
import com.android.settingslib.applications.ApplicationsState.AppFilter;
import java.util.List;
+
/*
* Connects info of apps that change wifi state to the ApplicationsState. Wraps around the generic
* AppStateAppOpsBridge class to tailor to the semantics of CHANGE_WIFI_STATE. Also provides app
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
index 11f3612..cbc89ae 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogFragment.java
@@ -26,7 +26,6 @@
import android.graphics.drawable.Drawable;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
import android.os.Bundle;
import android.os.Handler;
diff --git a/src/com/android/settings/wifi/WifiAPITest.java b/src/com/android/settings/wifi/WifiAPITest.java
index 87499f5..15465ed 100644
--- a/src/com/android/settings/wifi/WifiAPITest.java
+++ b/src/com/android/settings/wifi/WifiAPITest.java
@@ -32,7 +32,6 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-
/**
* Provide an interface for testing out the Wifi API
*/
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index e3ca575..408ffbe 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -79,6 +79,7 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -319,9 +320,9 @@
// Display IP address.
StaticIpConfiguration staticConfig = config.getIpConfiguration()
.getStaticIpConfiguration();
- if (staticConfig != null && staticConfig.ipAddress != null) {
+ if (staticConfig != null && staticConfig.getIpAddress() != null) {
addRow(group, R.string.wifi_ip_address,
- staticConfig.ipAddress.getAddress().getHostAddress());
+ staticConfig.getIpAddress().getAddress().getHostAddress());
}
} else {
mIpSettingsSpinner.setSelection(DHCP);
@@ -811,9 +812,12 @@
.get(mEapSimSpinner.getSelectedItemPosition()).getCarrierId();
}
- config.setIpConfiguration(
- new IpConfiguration(mIpAssignment, mProxySettings,
- mStaticIpConfiguration, mHttpProxy));
+ final IpConfiguration ipConfig = new IpConfiguration();
+ ipConfig.setIpAssignment(mIpAssignment);
+ ipConfig.setProxySettings(mProxySettings);
+ ipConfig.setStaticIpConfiguration(mStaticIpConfiguration);
+ ipConfig.setHttpProxy(mHttpProxy);
+ config.setIpConfiguration(ipConfig);
if (mMeteredSettingsSpinner != null) {
config.meteredOverride = mMeteredSettingsSpinner.getSelectedItemPosition();
}
@@ -860,7 +864,8 @@
result = R.string.proxy_error_invalid_port;
}
if (result == 0) {
- mHttpProxy = new ProxyInfo(host, port, exclusionList);
+ mHttpProxy = ProxyInfo.buildDirectProxy(
+ host, port, Arrays.asList(exclusionList.split(",")));
} else {
return false;
}
@@ -874,7 +879,7 @@
if (uri == null) {
return false;
}
- mHttpProxy = new ProxyInfo(uri);
+ mHttpProxy = ProxyInfo.buildPacProxy(uri);
}
return true;
}
@@ -897,67 +902,81 @@
if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) {
return R.string.wifi_ip_settings_invalid_ip_address;
}
-
- int networkPrefixLength = -1;
+ // Copy all fields into the builder first and set desired value later with builder.
+ final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder()
+ .setDnsServers(staticIpConfiguration.getDnsServers())
+ .setDomains(staticIpConfiguration.getDomains())
+ .setGateway(staticIpConfiguration.getGateway())
+ .setIpAddress(staticIpConfiguration.getIpAddress());
try {
- networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
- if (networkPrefixLength < 0 || networkPrefixLength > 32) {
- return R.string.wifi_ip_settings_invalid_network_prefix_length;
- }
- staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength);
- } catch (NumberFormatException e) {
- // Set the hint as default after user types in ip address
- mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
- R.string.wifi_network_prefix_length_hint));
- } catch (IllegalArgumentException e) {
- return R.string.wifi_ip_settings_invalid_ip_address;
- }
-
- String gateway = mGatewayView.getText().toString();
- if (TextUtils.isEmpty(gateway)) {
+ int networkPrefixLength = -1;
try {
- //Extract a default gateway from IP address
- InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength);
- byte[] addr = netPart.getAddress();
- addr[addr.length - 1] = 1;
- mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
- } catch (RuntimeException ee) {
- } catch (java.net.UnknownHostException u) {
+ networkPrefixLength = Integer.parseInt(
+ mNetworkPrefixLengthView.getText().toString());
+ if (networkPrefixLength < 0 || networkPrefixLength > 32) {
+ return R.string.wifi_ip_settings_invalid_network_prefix_length;
+ }
+ staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength));
+ } catch (NumberFormatException e) {
+ // Set the hint as default after user types in ip address
+ mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
+ R.string.wifi_network_prefix_length_hint));
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_ip_address;
}
- } else {
- InetAddress gatewayAddr = getIPv4Address(gateway);
- if (gatewayAddr == null) {
- return R.string.wifi_ip_settings_invalid_gateway;
- }
- if (gatewayAddr.isMulticastAddress()) {
- return R.string.wifi_ip_settings_invalid_gateway;
- }
- staticIpConfiguration.gateway = gatewayAddr;
- }
- String dns = mDns1View.getText().toString();
- InetAddress dnsAddr = null;
-
- if (TextUtils.isEmpty(dns)) {
- //If everything else is valid, provide hint as a default option
- mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
- } else {
- dnsAddr = getIPv4Address(dns);
- if (dnsAddr == null) {
- return R.string.wifi_ip_settings_invalid_dns;
+ String gateway = mGatewayView.getText().toString();
+ if (TextUtils.isEmpty(gateway)) {
+ try {
+ //Extract a default gateway from IP address
+ InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength);
+ byte[] addr = netPart.getAddress();
+ addr[addr.length - 1] = 1;
+ mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
+ } catch (RuntimeException ee) {
+ } catch (java.net.UnknownHostException u) {
+ }
+ } else {
+ InetAddress gatewayAddr = getIPv4Address(gateway);
+ if (gatewayAddr == null) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ if (gatewayAddr.isMulticastAddress()) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ staticIPBuilder.setGateway(gatewayAddr);
}
- staticIpConfiguration.dnsServers.add(dnsAddr);
- }
- if (mDns2View.length() > 0) {
- dns = mDns2View.getText().toString();
- dnsAddr = getIPv4Address(dns);
- if (dnsAddr == null) {
- return R.string.wifi_ip_settings_invalid_dns;
+ String dns = mDns1View.getText().toString();
+ InetAddress dnsAddr = null;
+ final ArrayList<InetAddress> dnsServers = new ArrayList<>();
+
+ if (TextUtils.isEmpty(dns)) {
+ //If everything else is valid, provide hint as a default option
+ mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
+ } else {
+ dnsAddr = getIPv4Address(dns);
+ if (dnsAddr == null) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ dnsServers.add(dnsAddr);
}
- staticIpConfiguration.dnsServers.add(dnsAddr);
+
+ if (mDns2View.length() > 0) {
+ dns = mDns2View.getText().toString();
+ dnsAddr = getIPv4Address(dns);
+ if (dnsAddr == null) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ dnsServers.add(dnsAddr);
+ }
+ staticIPBuilder.setDnsServers(dnsServers);
+ return 0;
+ } finally {
+ // Caller of this method may rely on staticIpConfiguration, so build the final result
+ // at the end of the method.
+ staticIpConfiguration = staticIPBuilder.build();
}
- return 0;
}
private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) {
@@ -1366,18 +1385,18 @@
StaticIpConfiguration staticConfig = config.getIpConfiguration()
.getStaticIpConfiguration();
if (staticConfig != null) {
- if (staticConfig.ipAddress != null) {
+ if (staticConfig.getIpAddress() != null) {
mIpAddressView.setText(
- staticConfig.ipAddress.getAddress().getHostAddress());
- mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress
- .getPrefixLength()));
+ staticConfig.getIpAddress().getAddress().getHostAddress());
+ mNetworkPrefixLengthView.setText(Integer.toString(
+ staticConfig.getIpAddress().getPrefixLength()));
}
- if (staticConfig.gateway != null) {
- mGatewayView.setText(staticConfig.gateway.getHostAddress());
+ if (staticConfig.getGateway() != null) {
+ mGatewayView.setText(staticConfig.getGateway().getHostAddress());
}
- Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.iterator();
+ Iterator<InetAddress> dnsIterator = staticConfig.getDnsServers().iterator();
if (dnsIterator.hasNext()) {
mDns1View.setText(dnsIterator.next().getHostAddress());
}
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index e42c538..9f3b1a7 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -79,6 +79,7 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -305,9 +306,9 @@
// Display IP address.
StaticIpConfiguration staticConfig = config.getIpConfiguration()
.getStaticIpConfiguration();
- if (staticConfig != null && staticConfig.ipAddress != null) {
+ if (staticConfig != null && staticConfig.getIpAddress() != null) {
addRow(group, R.string.wifi_ip_address,
- staticConfig.ipAddress.getAddress().getHostAddress());
+ staticConfig.getIpAddress().getAddress().getHostAddress());
}
} else {
mIpSettingsSpinner.setSelection(DHCP);
@@ -774,9 +775,12 @@
return null;
}
- config.setIpConfiguration(
- new IpConfiguration(mIpAssignment, mProxySettings,
- mStaticIpConfiguration, mHttpProxy));
+ final IpConfiguration ipConfig = new IpConfiguration();
+ ipConfig.setIpAssignment(mIpAssignment);
+ ipConfig.setProxySettings(mProxySettings);
+ ipConfig.setStaticIpConfiguration(mStaticIpConfiguration);
+ ipConfig.setHttpProxy(mHttpProxy);
+ config.setIpConfiguration(ipConfig);
if (mMeteredSettingsSpinner != null) {
config.meteredOverride = mMeteredSettingsSpinner.getSelectedItemPosition();
}
@@ -822,7 +826,8 @@
result = R.string.proxy_error_invalid_port;
}
if (result == 0) {
- mHttpProxy = new ProxyInfo(host, port, exclusionList);
+ mHttpProxy = ProxyInfo.buildDirectProxy(
+ host, port, Arrays.asList(exclusionList.split(",")));
} else {
return false;
}
@@ -836,7 +841,7 @@
if (uri == null) {
return false;
}
- mHttpProxy = new ProxyInfo(uri);
+ mHttpProxy = ProxyInfo.buildPacProxy(uri);
}
return true;
}
@@ -860,66 +865,83 @@
return R.string.wifi_ip_settings_invalid_ip_address;
}
- int networkPrefixLength = -1;
+ // Copy all fields into the builder first and set desired value later with builder.
+ final StaticIpConfiguration.Builder staticIPBuilder = new StaticIpConfiguration.Builder()
+ .setDnsServers(staticIpConfiguration.getDnsServers())
+ .setDomains(staticIpConfiguration.getDomains())
+ .setGateway(staticIpConfiguration.getGateway())
+ .setIpAddress(staticIpConfiguration.getIpAddress());
try {
- networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
- if (networkPrefixLength < 0 || networkPrefixLength > 32) {
- return R.string.wifi_ip_settings_invalid_network_prefix_length;
- }
- staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, networkPrefixLength);
- } catch (NumberFormatException e) {
- // Set the hint as default after user types in ip address
- mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
- R.string.wifi_network_prefix_length_hint));
- } catch (IllegalArgumentException e) {
- return R.string.wifi_ip_settings_invalid_ip_address;
- }
-
- String gateway = mGatewayView.getText().toString();
- if (TextUtils.isEmpty(gateway)) {
+ int networkPrefixLength = -1;
try {
- //Extract a default gateway from IP address
- InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength);
- byte[] addr = netPart.getAddress();
- addr[addr.length - 1] = 1;
- mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
- } catch (RuntimeException ee) {
- } catch (java.net.UnknownHostException u) {
+ networkPrefixLength =
+ Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
+ if (networkPrefixLength < 0 || networkPrefixLength > 32) {
+ return R.string.wifi_ip_settings_invalid_network_prefix_length;
+ }
+ staticIPBuilder.setIpAddress(new LinkAddress(inetAddr, networkPrefixLength));
+ } catch (NumberFormatException e) {
+ // Set the hint as default after user types in ip address
+ mNetworkPrefixLengthView.setText(mConfigUi.getContext().getString(
+ R.string.wifi_network_prefix_length_hint));
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_ip_address;
}
- } else {
- InetAddress gatewayAddr = getIPv4Address(gateway);
- if (gatewayAddr == null) {
- return R.string.wifi_ip_settings_invalid_gateway;
- }
- if (gatewayAddr.isMulticastAddress()) {
- return R.string.wifi_ip_settings_invalid_gateway;
- }
- staticIpConfiguration.gateway = gatewayAddr;
- }
- String dns = mDns1View.getText().toString();
- InetAddress dnsAddr = null;
-
- if (TextUtils.isEmpty(dns)) {
- //If everything else is valid, provide hint as a default option
- mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
- } else {
- dnsAddr = getIPv4Address(dns);
- if (dnsAddr == null) {
- return R.string.wifi_ip_settings_invalid_dns;
+ String gateway = mGatewayView.getText().toString();
+ if (TextUtils.isEmpty(gateway)) {
+ try {
+ //Extract a default gateway from IP address
+ InetAddress netPart = NetUtils.getNetworkPart(inetAddr, networkPrefixLength);
+ byte[] addr = netPart.getAddress();
+ addr[addr.length - 1] = 1;
+ mGatewayView.setText(InetAddress.getByAddress(addr).getHostAddress());
+ } catch (RuntimeException ee) {
+ } catch (java.net.UnknownHostException u) {
+ }
+ } else {
+ InetAddress gatewayAddr = getIPv4Address(gateway);
+ if (gatewayAddr == null) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ if (gatewayAddr.isMulticastAddress()) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ staticIPBuilder.setGateway(gatewayAddr);
}
- staticIpConfiguration.dnsServers.add(dnsAddr);
- }
- if (mDns2View.length() > 0) {
- dns = mDns2View.getText().toString();
- dnsAddr = getIPv4Address(dns);
- if (dnsAddr == null) {
- return R.string.wifi_ip_settings_invalid_dns;
+ String dns = mDns1View.getText().toString();
+ InetAddress dnsAddr = null;
+ final ArrayList<InetAddress> dnsServers = new ArrayList<>();
+
+ if (TextUtils.isEmpty(dns)) {
+ //If everything else is valid, provide hint as a default option
+ mDns1View.setText(mConfigUi.getContext().getString(R.string.wifi_dns1_hint));
+ } else {
+ dnsAddr = getIPv4Address(dns);
+ if (dnsAddr == null) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ dnsServers.add(dnsAddr);
+ staticIpConfiguration.getDnsServers().add(dnsAddr);
}
- staticIpConfiguration.dnsServers.add(dnsAddr);
+
+ if (mDns2View.length() > 0) {
+ dns = mDns2View.getText().toString();
+ dnsAddr = getIPv4Address(dns);
+ if (dnsAddr == null) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ dnsServers.add(dnsAddr);
+ staticIpConfiguration.getDnsServers().add(dnsAddr);
+ }
+ staticIPBuilder.setDnsServers(dnsServers);
+ return 0;
+ } finally {
+ // Caller of this method may rely on staticIpConfiguration, so build the final result
+ // at the end of the method.
+ staticIpConfiguration = staticIPBuilder.build();
}
- return 0;
}
private void showSecurityFields(boolean refreshEapMethods, boolean refreshCertificates) {
@@ -1330,18 +1352,18 @@
StaticIpConfiguration staticConfig = config.getIpConfiguration()
.getStaticIpConfiguration();
if (staticConfig != null) {
- if (staticConfig.ipAddress != null) {
+ if (staticConfig.getIpAddress() != null) {
mIpAddressView.setText(
- staticConfig.ipAddress.getAddress().getHostAddress());
- mNetworkPrefixLengthView.setText(Integer.toString(staticConfig.ipAddress
- .getPrefixLength()));
+ staticConfig.getIpAddress().getAddress().getHostAddress());
+ mNetworkPrefixLengthView.setText(Integer.toString(
+ staticConfig.getIpAddress().getPrefixLength()));
}
- if (staticConfig.gateway != null) {
- mGatewayView.setText(staticConfig.gateway.getHostAddress());
+ if (staticConfig.getGateway() != null) {
+ mGatewayView.setText(staticConfig.getGateway().getHostAddress());
}
- Iterator<InetAddress> dnsIterator = staticConfig.dnsServers.iterator();
+ Iterator<InetAddress> dnsIterator = staticConfig.getDnsServers().iterator();
if (dnsIterator.hasNext()) {
mDns1View.setText(dnsIterator.next().getHostAddress());
}
diff --git a/src/com/android/settings/wifi/WifiConfigInfo.java b/src/com/android/settings/wifi/WifiConfigInfo.java
index f042feb..0de3063 100644
--- a/src/com/android/settings/wifi/WifiConfigInfo.java
+++ b/src/com/android/settings/wifi/WifiConfigInfo.java
@@ -26,7 +26,6 @@
import java.util.List;
-
/**
* Configuration details saved by the user on the WifiSettings screen
*/
diff --git a/src/com/android/settings/wifi/WifiDialogActivity.java b/src/com/android/settings/wifi/WifiDialogActivity.java
index 1c5a8ed..877933e 100644
--- a/src/com/android/settings/wifi/WifiDialogActivity.java
+++ b/src/com/android/settings/wifi/WifiDialogActivity.java
@@ -21,7 +21,6 @@
import android.net.NetworkInfo;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiManager.ActionListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
diff --git a/src/com/android/settings/wifi/WifiInfo.java b/src/com/android/settings/wifi/WifiInfo.java
index a131f18..0d6d63d 100644
--- a/src/com/android/settings/wifi/WifiInfo.java
+++ b/src/com/android/settings/wifi/WifiInfo.java
@@ -22,7 +22,6 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
-
/**
* Wifi information menu item on the diagnostic screen
*/
diff --git a/src/com/android/settings/wifi/WifiNoInternetDialog.java b/src/com/android/settings/wifi/WifiNoInternetDialog.java
index 9d90b6a..57e42f8 100644
--- a/src/com/android/settings/wifi/WifiNoInternetDialog.java
+++ b/src/com/android/settings/wifi/WifiNoInternetDialog.java
@@ -70,22 +70,17 @@
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
- if (intent == null || !isKnownAction(intent) || !"netId".equals(intent.getScheme())) {
+ if (intent == null || !isKnownAction(intent)) {
Log.e(TAG, "Unexpected intent " + intent + ", exiting");
finish();
return;
}
mAction = intent.getAction();
-
- try {
- mNetwork = new Network(Integer.parseInt(intent.getData().getSchemeSpecificPart()));
- } catch (NullPointerException|NumberFormatException e) {
- mNetwork = null;
- }
+ mNetwork = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
if (mNetwork == null) {
- Log.e(TAG, "Can't determine network from '" + intent.getData() + "' , exiting");
+ Log.e(TAG, "Can't determine network from intent extra, exiting");
finish();
return;
}
diff --git a/src/com/android/settings/wifi/WifiStatusTest.java b/src/com/android/settings/wifi/WifiStatusTest.java
index ca7f5f7..249cd71 100644
--- a/src/com/android/settings/wifi/WifiStatusTest.java
+++ b/src/com/android/settings/wifi/WifiStatusTest.java
@@ -45,7 +45,6 @@
import java.net.UnknownHostException;
import java.util.List;
-
/**
* Show the current status details of Wifi related fields
*/
diff --git a/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
index a44fcbe..b91b39d 100644
--- a/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
+++ b/src/com/android/settings/wifi/calling/ListWithEntrySummaryPreference.java
@@ -30,7 +30,9 @@
import android.widget.ListAdapter;
import android.widget.RadioButton;
import android.widget.TextView;
+
import androidx.appcompat.app.AlertDialog.Builder;
+
import com.android.settings.CustomListPreference;
import com.android.settings.R;
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index 024c1c3..de64b91 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -29,6 +29,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
@@ -102,7 +103,10 @@
private ProvisioningManager mProvisioningManager;
private TelephonyManager mTelephonyManager;
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ private final PhoneTelephonyCallback mTelephonyCallback = new PhoneTelephonyCallback();
+
+ private class PhoneTelephonyCallback extends TelephonyCallback implements
+ TelephonyCallback.CallStateListener {
/*
* Enable/disable controls when in/out of a call and depending on
* TTY mode and TTY support over VoLTE.
@@ -110,7 +114,7 @@
* java.lang.String)
*/
@Override
- public void onCallStateChanged(int state, String incomingNumber) {
+ public void onCallStateChanged(int state) {
final SettingsActivity activity = (SettingsActivity) getActivity();
final boolean isNonTtyOrTtyOnVolteEnabled =
queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl();
@@ -149,7 +153,7 @@
&& isCallStateIdle);
}
}
- };
+ }
/*
* Launch carrier emergency address managemnent activity
@@ -398,7 +402,7 @@
res.getStringArray(R.array.wifi_calling_mode_summaries_without_wifi_only));
}
- // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
+ // NOTE: Buttons will be enabled/disabled in mTelephonyCallback
final WifiCallingQueryImsState queryIms = queryImsState(mSubId);
final boolean wfcEnabled = queryIms.isEnabledByUser()
&& queryIms.isAllowUserControl();
@@ -416,16 +420,16 @@
updateBody();
+ final Context context = getActivity();
if (queryImsState(mSubId).isWifiCallingSupported()) {
- getTelephonyManagerForSub(mSubId).listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_CALL_STATE);
+ getTelephonyManagerForSub(mSubId).registerTelephonyCallback(
+ context.getMainExecutor(), mTelephonyCallback);
mSwitchBar.addOnSwitchChangeListener(this);
mValidListener = true;
}
- final Context context = getActivity();
context.registerReceiver(mIntentReceiver, mIntentFilter);
final Intent intent = getActivity().getIntent();
@@ -446,8 +450,7 @@
if (mValidListener) {
mValidListener = false;
- getTelephonyManagerForSub(mSubId).listen(mPhoneStateListener,
- PhoneStateListener.LISTEN_NONE);
+ getTelephonyManagerForSub(mSubId).unregisterTelephonyCallback(mTelephonyCallback);
mSwitchBar.removeOnSwitchChangeListener(this);
}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
index 4467d66..3636341 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
@@ -43,7 +43,6 @@
import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
-import com.android.ims.ImsConfig;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.network.ims.WifiCallingQueryImsState;
@@ -57,7 +56,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-
/**
* Helper class to control slices for wifi calling settings.
*/
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index 2d9d444..211ceed 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -844,7 +844,8 @@
// Find IPv4 default gateway.
String gateway = null;
for (RouteInfo routeInfo : mLinkProperties.getRoutes()) {
- if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) {
+ if (routeInfo.hasGateway() && routeInfo.isDefaultRoute()
+ && routeInfo.getDestination().getAddress() instanceof Inet4Address) {
gateway = routeInfo.getGateway().getHostAddress();
break;
}
diff --git a/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
index 2a93005..52645aa 100644
--- a/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
@@ -27,7 +27,6 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.wifi.WifiDialog2;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.wifitrackerlib.WifiEntry;
/**
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
index d6e1b60..c484922 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
@@ -28,7 +28,6 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.wifi.WifiDialog2;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.wifitrackerlib.WifiEntry;
/**
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
index aa73a17..ae45251 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
@@ -19,8 +19,6 @@
import android.content.Context;
import android.net.Uri;
-import com.android.settings.slices.SliceBackgroundWorker;
-
/**
* {@link SliceBackgroundWorker} for Wi-Fi, used by {@link ContextualWifiSlice}.
*/
diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
new file mode 100644
index 0000000..bc87d5c
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 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.wifi.tether;
+
+import android.content.Context;
+import android.net.wifi.SoftApConfiguration;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+/**
+ * This controller helps to manage the state of maximize compatibility switch preference.
+ */
+public class WifiTetherMaximizeCompatibilityPreferenceController extends
+ WifiTetherBasePreferenceController {
+
+ private static final String TAG = "WifiTetherMaximizeCompatibilityPref";
+ public static final String PREF_KEY = "wifi_tether_maximize_compatibility";
+
+ private boolean mIsChecked;
+
+ public WifiTetherMaximizeCompatibilityPreferenceController(Context context,
+ WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
+ super(context, listener);
+ mIsChecked = isMaximizeCompatibilityEnabled();
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return PREF_KEY;
+ }
+
+ @Override
+ public void updateDisplay() {
+ if (mPreference == null) {
+ return;
+ }
+ mPreference.setEnabled(is5GhzBandSupported());
+ ((SwitchPreference) mPreference).setChecked(mIsChecked);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mIsChecked = (Boolean) newValue;
+ if (mListener != null) {
+ mListener.onTetherConfigUpdated(this);
+ }
+ return true;
+ }
+
+ private boolean is5GhzBandSupported() {
+ if (mWifiManager == null) {
+ return false;
+ }
+ if (!mWifiManager.is5GHzBandSupported() || mWifiManager.getCountryCode() == null) {
+ return false;
+ }
+ return true;
+ }
+
+ @VisibleForTesting
+ boolean isMaximizeCompatibilityEnabled() {
+ if (mWifiManager == null) {
+ return false;
+ }
+ final SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
+ if (config == null) {
+ return false;
+ }
+ if (mWifiManager.isBridgedApConcurrencySupported()) {
+ final boolean isEnabled = config.isBridgedModeOpportunisticShutdownEnabled();
+ Log.d(TAG, "isBridgedModeOpportunisticShutdownEnabled:" + isEnabled);
+ return isEnabled;
+ }
+
+ // If the BridgedAp Concurrency is not supported in early Pixel devices (e.g. Pixel 2~5),
+ // show toggle on if the band includes SoftApConfiguration.BAND_5GHZ.
+ final int band = config.getBand();
+ Log.d(TAG, "getBand:" + band);
+ return (band & SoftApConfiguration.BAND_5GHZ) > 0;
+ }
+
+ /**
+ * Setup the Maximize Compatibility setting to the SoftAp Configuration
+ *
+ * @param builder The builder to build the SoftApConfiguration.
+ */
+ public void setupMaximizeCompatibility(SoftApConfiguration.Builder builder) {
+ if (builder == null) {
+ return;
+ }
+ final boolean enabled = mIsChecked;
+ if (mWifiManager.isBridgedApConcurrencySupported()) {
+ int[] bands = {
+ SoftApConfiguration.BAND_2GHZ,
+ SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ};
+ builder.setBands(bands);
+ Log.d(TAG, "setBridgedModeOpportunisticShutdownEnabled:" + enabled);
+ builder.setBridgedModeOpportunisticShutdownEnabled(enabled);
+ } else {
+ int band = enabled
+ ? SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ
+ : SoftApConfiguration.BAND_2GHZ;
+ Log.d(TAG, "setBand:" + band);
+ builder.setBand(band);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index 939f077..e342550 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -54,8 +54,7 @@
private static final String TAG = "WifiTetherSettings";
private static final IntentFilter TETHER_STATE_CHANGE_FILTER;
private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen";
- private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3;
- private static final int EXPANDED_CHILD_COUNT_DEFAULT = 4;
+ private static final int EXPANDED_CHILD_COUNT_DEFAULT = 3;
@VisibleForTesting
static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name";
@@ -64,13 +63,14 @@
@VisibleForTesting
static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off";
@VisibleForTesting
- static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band";
+ static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY =
+ WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY;
private WifiTetherSwitchBarController mSwitchBarController;
private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
- private WifiTetherApBandPreferenceController mApBandPreferenceController;
private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
+ private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
private WifiManager mWifiManager;
private boolean mRestartWifiApAfterConfigChange;
@@ -116,7 +116,8 @@
mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class);
mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class);
mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class);
- mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class);
+ mMaxCompatibilityPrefController =
+ use(WifiTetherMaximizeCompatibilityPreferenceController.class);
}
@Override
@@ -180,10 +181,9 @@
controllers.add(new WifiTetherSSIDPreferenceController(context, listener));
controllers.add(new WifiTetherSecurityPreferenceController(context, listener));
controllers.add(new WifiTetherPasswordPreferenceController(context, listener));
- controllers.add(new WifiTetherApBandPreferenceController(context, listener));
controllers.add(
new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF));
-
+ controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener));
return controllers;
}
@@ -219,7 +219,7 @@
mPasswordPreferenceController.getPasswordValidated(securityType),
securityType);
}
- configBuilder.setBand(mApBandPreferenceController.getBandIndex());
+ mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder);
return configBuilder.build();
}
@@ -229,14 +229,10 @@
}
private void updateDisplayWithNewConfig() {
- use(WifiTetherSSIDPreferenceController.class)
- .updateDisplay();
- use(WifiTetherSecurityPreferenceController.class)
- .updateDisplay();
- use(WifiTetherPasswordPreferenceController.class)
- .updateDisplay();
- use(WifiTetherApBandPreferenceController.class)
- .updateDisplay();
+ use(WifiTetherSSIDPreferenceController.class).updateDisplay();
+ use(WifiTetherSecurityPreferenceController.class).updateDisplay();
+ use(WifiTetherPasswordPreferenceController.class).updateDisplay();
+ use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -250,7 +246,7 @@
keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
keys.add(KEY_WIFI_TETHER_AUTO_OFF);
- keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND);
+ keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
// Remove duplicate
@@ -294,22 +290,17 @@
private void reConfigInitialExpandedChildCount() {
final PreferenceGroup screen = getPreferenceScreen();
- if (mSecurityPreferenceController.getSecurityType()
- == SoftApConfiguration.SECURITY_TYPE_OPEN) {
- screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_WITH_SECURITY_NON);
- return;
+ if (screen != null) {
+ screen.setInitialExpandedChildrenCount(getInitialExpandedChildCount());
}
- screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_DEFAULT);
}
@Override
public int getInitialExpandedChildCount() {
- if (mSecurityPreferenceController == null) {
- return EXPANDED_CHILD_COUNT_DEFAULT;
+ if (mSecurityPreferenceController != null && mSecurityPreferenceController.getSecurityType()
+ == SoftApConfiguration.SECURITY_TYPE_OPEN) {
+ return (EXPANDED_CHILD_COUNT_DEFAULT - 1);
}
-
- return (mSecurityPreferenceController.getSecurityType()
- == SoftApConfiguration.SECURITY_TYPE_OPEN)
- ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT;
+ return EXPANDED_CHILD_COUNT_DEFAULT;
}
}
diff --git a/tests/legacy_unit/src/com/android/settings/vpn2/AppSettingsTest.java b/tests/legacy_unit/src/com/android/settings/vpn2/AppSettingsTest.java
index 014d8ea..a30d610 100644
--- a/tests/legacy_unit/src/com/android/settings/vpn2/AppSettingsTest.java
+++ b/tests/legacy_unit/src/com/android/settings/vpn2/AppSettingsTest.java
@@ -17,6 +17,7 @@
package com.android.settings.vpn2;
import static com.android.settings.vpn2.AppManagementFragment.appHasVpnPermission;
+
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.when;
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
new file mode 100644
index 0000000..473b566
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonFragmentTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.testutils.XmlTestUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+/** Tests for {@link AccessibilityButtonFragment}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonFragmentTest {
+
+ private Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void getNonIndexableKeys_existInXmlLayout() {
+ final List<String> niks = AccessibilityButtonFragment.SEARCH_INDEX_DATA_PROVIDER
+ .getNonIndexableKeys(mContext);
+ final List<String> keys =
+ XmlTestUtils.getKeysFromPreferenceXml(mContext,
+ R.xml.accessibility_button_settings);
+
+ assertThat(keys).containsAtLeastElementsIn(niks);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
new file mode 100644
index 0000000..a67038a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonLocationPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonLocationPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonLocationPreferenceControllerTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Spy
+ private final Resources mResources = mContext.getResources();
+ private final ContentResolver mContentResolver = mContext.getContentResolver();
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private AccessibilityButtonLocationPreferenceController mController;
+
+
+ @Before
+ public void setUp() {
+ mController = new AccessibilityButtonLocationPreferenceController(mContext,
+ "test_key");
+ when(mContext.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureEnabled_returnDisabledDependentSetting() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_GESTURAL);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void getAvailabilityStatus_navigationGestureDisabled_returnAvailable() {
+ when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode))
+ .thenReturn(NAV_BAR_MODE_2BUTTON);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void updateState_a11yBtnModeNavigationBar_navigationBarValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.updateState(mListPreference);
+
+ final String navigationBarValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+ assertThat(mListPreference.getValue()).isEqualTo(navigationBarValue);
+ }
+
+ @Test
+ public void onPreferenceChange_a11yBtnModeFloatingMenu_floatingMenuValue() {
+ final String floatingMenuValue = String.valueOf(ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ mController.onPreferenceChange(mListPreference, floatingMenuValue);
+
+ assertThat(mListPreference.getValue()).isEqualTo(floatingMenuValue);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
new file mode 100644
index 0000000..eb88175
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreviewPreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.provider.Settings;
+import android.widget.ImageView;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link AccessibilityButtonPreviewPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class AccessibilityButtonPreviewPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private AccessibilityButtonPreviewPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new AccessibilityButtonPreviewPreferenceController(mContext, "test_key");
+ mController.mPreview = new ImageView(mContext);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeNavigationBar_getNavigationBarDrawable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable navigationBarDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_navigation);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ navigationBarDrawable.getConstantState());
+ }
+
+ @Test
+ public void onChange_updatePreviewPreferenceWithConfig_expectedPreviewDrawable() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, /* small size */ 0);
+ Settings.Secure.putFloat(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.1f);
+
+ mController.mContentObserver.onChange(false);
+
+ final Drawable smallFloatingMenuWithTenOpacityDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext,
+ R.drawable.accessibility_button_preview_small_floating_menu, 10);
+ assertThat(mController.mPreview.getDrawable().getConstantState()).isEqualTo(
+ smallFloatingMenuWithTenOpacityDrawable.getConstantState());
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
new file mode 100644
index 0000000..5cf87ee
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuFadePreferenceControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuFadePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuFadePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ private static final int OFF = 0;
+ private static final int ON = 1;
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final SwitchPreference mSwitchPreference = new SwitchPreference(mContext);
+ private FloatingMenuFadePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuFadePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_keyFloatingMenuFadeDisabled_fadeIsDisabled() {
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.updateState(mSwitchPreference);
+
+ assertThat(mSwitchPreference.isChecked()).isFalse();
+ }
+
+ @Test
+ public void onPreferenceChange_floatingMenuFadeEnabled_keyFloatingMenuFadeIsOn() {
+ mController.onPreferenceChange(mSwitchPreference, Boolean.TRUE);
+
+ final int actualValue = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+ assertThat(actualValue).isEqualTo(ON);
+ }
+
+ @Test
+ public void onChange_floatingMenuFadeChangeToDisabled_preferenceDisabled() {
+ mController.mPreference = mSwitchPreference;
+ Settings.Secure.putInt(mContentResolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED, OFF);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
new file mode 100644
index 0000000..ec449d2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuLayerDrawableTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuLayerDrawable}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuLayerDrawableTest {
+
+ private static final int TEST_RES_ID =
+ com.android.internal.R.drawable.ic_accessibility_magnification;
+ private static final int TEST_RES_ID_2 =
+ com.android.internal.R.drawable.ic_accessibility_color_inversion;
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+
+ @Test
+ public void createLayerDrawable_configCorrect() {
+ final Drawable expected1stDrawable = mContext.getDrawable(
+ R.drawable.accessibility_button_preview_base);
+ final Drawable expected2ndDrawable = mContext.getDrawable(TEST_RES_ID);
+
+ final FloatingMenuLayerDrawable actualDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID,
+ /* opacity= */ 27);
+
+ final Drawable actual1stDrawable = actualDrawable.getDrawable(0);
+ final Drawable actual2ndDrawable = actualDrawable.getDrawable(1);
+ // These are VectorDrawables, so it can use getConstantState() to compare.
+ assertThat(actual1stDrawable.getConstantState()).isEqualTo(
+ expected1stDrawable.getConstantState());
+ assertThat(actual2ndDrawable.getConstantState()).isEqualTo(
+ expected2ndDrawable.getConstantState());
+ }
+
+ @Test
+ public void updateLayerDrawable_expectedFloatingMenuLayerDrawableState() {
+ final FloatingMenuLayerDrawable originalDrawable =
+ FloatingMenuLayerDrawable.createLayerDrawable(mContext, TEST_RES_ID, /* opacity= */
+ 72);
+
+ originalDrawable.updateLayerDrawable(mContext, TEST_RES_ID_2, /* opacity= */ 27);
+
+ assertThat(originalDrawable.getConstantState()).isEqualTo(
+ new FloatingMenuLayerDrawable.FloatingMenuLayerDrawableState(mContext,
+ TEST_RES_ID_2, /* opacity= */ 27));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
new file mode 100644
index 0000000..1638f90
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuOpacityPreferenceControllerTest.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.DEFAULT_OPACITY;
+import static com.android.settings.accessibility.FloatingMenuOpacityPreferenceController.PRECISION;
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.widget.SeekBarPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuOpacityPreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuOpacityPreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private FloatingMenuOpacityPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuOpacityPreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = new SeekBarPreference(mContext);
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void getSliderPosition_putNormalOpacityValue_expectedValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.35f);
+
+ assertThat(mController.getSliderPosition()).isEqualTo(35);
+ }
+
+ @Test
+ public void getSliderPosition_putOutOfBoundOpacityValue_defaultValue() {
+ Settings.Secure.putFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, 0.01f);
+
+ final int defaultValue = Math.round(DEFAULT_OPACITY * PRECISION);
+ assertThat(mController.getSliderPosition()).isEqualTo(defaultValue);
+ }
+
+ @Test
+ public void setSliderPosition_expectedValue() {
+ mController.setSliderPosition(27);
+
+ final float value = Settings.Secure.getFloat(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY, -1);
+ assertThat(value).isEqualTo(0.27f);
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
+ false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
new file mode 100644
index 0000000..4d7d98d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/FloatingMenuSizePreferenceControllerTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.DISABLED_DEPENDENT_SETTING;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.ListPreference;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests for {@link FloatingMenuSizePreferenceController}. */
+@RunWith(RobolectricTestRunner.class)
+public class FloatingMenuSizePreferenceControllerTest {
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Spy
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ private ContentResolver mContentResolver;
+ private final ListPreference mListPreference = new ListPreference(mContext);
+ private FloatingMenuSizePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mController = new FloatingMenuSizePreferenceController(mContext, "test_key");
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeFloatingMenu_returnAvailable() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_a11yBtnModeNavigationBar_returnDisabledDependentSetting() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ public void updateState_floatingMenuLargeSizeAndFullCircle_largeSizeValue() {
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+ FloatingMenuSizePreferenceController.Size.LARGE);
+
+ mController.updateState(mListPreference);
+
+ final String largeSize = String.valueOf(FloatingMenuSizePreferenceController.Size.LARGE);
+ assertThat(mListPreference.getValue()).isEqualTo(largeSize);
+ }
+
+ @Test
+ public void onChange_a11yBtnModeChangeToNavigationBar_preferenceDisabled() {
+ mController.mPreference = mListPreference;
+ Settings.Secure.putInt(mContentResolver, Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+ ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR);
+
+ mController.mContentObserver.onChange(false);
+
+ assertThat(mController.mPreference.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onResume_registerSpecificContentObserver() {
+ mController.onResume();
+
+ verify(mContentResolver).registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_MODE), false,
+ mController.mContentObserver);
+ }
+
+ @Test
+ public void onPause_unregisterContentObserver() {
+ mController.onPause();
+
+ verify(mContentResolver).unregisterContentObserver(mController.mContentObserver);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
index d49213c..11128e4 100644
--- a/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/MagnificationSettingsFragmentTest.java
@@ -16,12 +16,15 @@
package com.android.settings.accessibility;
+import static com.android.settings.accessibility.MagnificationCapabilities.MagnificationMode;
import static com.android.settings.accessibility.MagnificationPreferenceFragment.ON;
+import static com.android.settings.accessibility.MagnificationSettingsFragment.MagnificationModeInfo;
import static com.google.common.truth.Truth.assertThat;
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.verify;
import static org.mockito.Mockito.when;
@@ -29,7 +32,8 @@
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings;
-import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceManager;
@@ -77,7 +81,7 @@
}
@Test
- public void onCreateDialog_capabilitiesInBundle_matchCheckBoxStatus() {
+ public void onCreateDialog_capabilitiesInBundle_checkedModeInDialogIsExpected() {
final Bundle windowModeSavedInstanceState = new Bundle();
windowModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
@@ -85,23 +89,21 @@
mFragment.onCreate(windowModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isFalse();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isTrue();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.WINDOW);
}
@Test
- public void onCreateDialog_capabilitiesInSettings_matchCheckBoxStatus() {
+ public void onCreateDialog_capabilitiesInSetting_checkedModeInDialogIsExpected() {
MagnificationCapabilities.setCapabilities(mContext,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mFragment.onCreate(Bundle.EMPTY);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isFalse();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.FULLSCREEN);
}
@Test
- public void onCreateDialog_capabilitiesInSettingsAndBundle_matchBundleValueCheckBoxStatus() {
+ public void onCreateDialog_choseModeIsDifferentFromInSettings_ShowUsersChoseModeInDialog() {
final Bundle allModeSavedInstanceState = new Bundle();
allModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL);
@@ -111,37 +113,93 @@
mFragment.onCreate(allModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isTrue();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.ALL);
}
@Test
- public void onCreateDialog_emptySettingsAndBundle_matchDefaultValueCheckBoxStatus() {
+ public void onCreateDialog_emptySettingsAndBundle_checkedModeInDialogIsDefaultValue() {
mFragment.onCreate(Bundle.EMPTY);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- // Compare to default Capabilities
- assertThat(mFragment.mMagnifyFullScreenCheckBox.isChecked()).isTrue();
- assertThat(mFragment.mMagnifyWindowCheckBox.isChecked()).isFalse();
+ assertThat(getChoseModeFromDialog()).isEqualTo(MagnificationMode.FULLSCREEN);
}
@Test
- public void checkWindowModeCheckBox_tripleTapEnabled_showSwitchShortcutDialog() {
+ public void chooseWindowMode_tripleTapEnabled_showSwitchShortcutDialog() {
+ enableTripleTap();
final Bundle fullScreenModeSavedInstanceState = new Bundle();
fullScreenModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
mFragment.onCreate(fullScreenModeSavedInstanceState);
mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
- enableTripleTap();
- final View dialogWidowView = mFragment.mDialog.findViewById(R.id.magnify_window_screen);
- final View dialogWindowTextArea = dialogWidowView.findViewById(R.id.container);
- dialogWindowTextArea.performClick();
+ performItemClickWith(MagnificationMode.WINDOW);
verify(mFragment).showDialog(
MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
}
+ @Test
+ public void chooseModeAll_tripleTapEnabled_showSwitchShortcutDialog() {
+ enableTripleTap();
+ final Bundle fullScreenModeSavedInstanceState = new Bundle();
+ fullScreenModeSavedInstanceState.putInt(EXTRA_CAPABILITY,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+ mFragment.onCreate(fullScreenModeSavedInstanceState);
+ mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
+
+ performItemClickWith(MagnificationMode.ALL);
+
+ verify(mFragment).showDialog(
+ MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+
+ @Test
+ public void chooseWindowMode_WindowModeInSettingsAndTripleTapEnabled_notShowShortCutDialog() {
+ enableTripleTap();
+ MagnificationCapabilities.setCapabilities(mContext,
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+ mFragment.onCreate(Bundle.EMPTY);
+ mFragment.onCreateDialog(MagnificationSettingsFragment.DIALOG_MAGNIFICATION_CAPABILITY);
+
+ performItemClickWith(MagnificationMode.WINDOW);
+
+ verify(mFragment, never()).showDialog(
+ MagnificationSettingsFragment.DIALOG_MAGNIFICATION_SWITCH_SHORTCUT);
+ }
+
+ private int getChoseModeFromDialog() {
+ final ListView listView = mFragment.mMagnificationModesListView;
+ assertThat(listView).isNotNull();
+
+ final int checkedPosition = listView.getCheckedItemPosition();
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) listView.getAdapter().getItem(
+ checkedPosition);
+ return modeInfo.mMagnificationMode;
+ }
+
+ private void performItemClickWith(@MagnificationMode int mode) {
+ final ListView listView = mFragment.mMagnificationModesListView;
+ assertThat(listView).isNotNull();
+
+ int modeIndex = AdapterView.NO_ID;
+ // Index 0 is header.
+ for (int i = 1; i < listView.getAdapter().getCount(); i++) {
+ final MagnificationModeInfo modeInfo =
+ (MagnificationModeInfo) listView.getAdapter().getItem(i);
+ if (modeInfo.mMagnificationMode == mode) {
+ modeIndex = i;
+ break;
+ }
+ }
+ if (modeIndex == AdapterView.NO_ID) {
+ throw new RuntimeException("The mode is not in the list.");
+ }
+
+ listView.performItemClick(listView.getChildAt(modeIndex), modeIndex, modeIndex);
+ }
+
private void enableTripleTap() {
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, ON);
diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
index 02b318f..534d3c6 100644
--- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
@@ -120,7 +120,6 @@
@Test
@Config(qualifiers = "mcc999",
shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void onStart_useMockAvatarViewMixin_shouldBeExecuted() {
diff --git a/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java b/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java
index 6ed266f..8725a0c 100644
--- a/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/ProviderPreferenceTest.java
@@ -16,7 +16,7 @@
package com.android.settings.accounts;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
index c735452..71ab334 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
@@ -30,7 +30,6 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.Bundle;
import android.os.UidBatteryConsumer;
@@ -39,15 +38,12 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.SettingsActivity;
import com.android.settings.fuelgauge.BatteryUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -73,13 +69,7 @@
@Mock
private UidBatteryConsumer mUidBatteryConsumer;
@Mock
- private BatterySipper mBatterySipper;
- @Mock
- private BatterySipper mOtherBatterySipper;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryStatsHelper;
- @Mock
- private BatteryStats.Uid mUid;
+ private UidBatteryConsumer mOtherUidBatteryConsumer;
@Mock
private PreferenceScreen mScreen;
@Mock
@@ -102,10 +92,8 @@
mBatteryPreference = spy(new Preference(RuntimeEnvironment.application));
- mBatterySipper.drainType = BatterySipper.DrainType.IDLE;
- mBatterySipper.uidObj = mUid;
- doReturn(TARGET_UID).when(mBatterySipper).getUid();
- doReturn(OTHER_UID).when(mOtherBatterySipper).getUid();
+ when(mUidBatteryConsumer.getUid()).thenReturn(TARGET_UID);
+ when(mOtherUidBatteryConsumer.getUid()).thenReturn(OTHER_UID);
mController = spy(new AppBatteryPreferenceController(
RuntimeEnvironment.application, mFragment, "package1", null /* lifecycle */));
@@ -125,14 +113,14 @@
}
@Test
- public void findTargetSipper_findCorrectSipper() {
- final List<BatterySipper> usageList = new ArrayList<>();
- usageList.add(mBatterySipper);
- usageList.add(mOtherBatterySipper);
- when(mBatteryStatsHelper.getUsageList()).thenReturn(usageList);
+ public void findTargetBatteryConsumer_findCorrectBatteryConsumer() {
+ final List<UidBatteryConsumer> uidBatteryConsumers = new ArrayList<>();
+ uidBatteryConsumers.add(mUidBatteryConsumer);
+ uidBatteryConsumers.add(mOtherUidBatteryConsumer);
+ when(mBatteryUsageStats.getUidBatteryConsumers()).thenReturn(uidBatteryConsumers);
- assertThat(mController.findTargetSipper(mBatteryStatsHelper, TARGET_UID))
- .isEqualTo(mBatterySipper);
+ assertThat(mController.findTargetUidBatteryConsumer(mBatteryUsageStats, TARGET_UID))
+ .isEqualTo(mUidBatteryConsumer);
}
@Test
@@ -147,13 +135,10 @@
@Test
public void updateBattery_hasBatteryStats_summaryPercent() {
- mController.mBatteryHelper = mBatteryStatsHelper;
- mController.mSipper = mBatterySipper;
mController.mBatteryUsageStats = mBatteryUsageStats;
mController.mUidBatteryConsumer = mUidBatteryConsumer;
doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(),
- anyDouble(), anyDouble(), anyInt());
- doReturn(new ArrayList<>()).when(mBatteryStatsHelper).getUsageList();
+ anyDouble(), anyInt());
mController.displayPreference(mScreen);
mController.updateBattery();
@@ -163,8 +148,6 @@
@Test
public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() {
- mController.mBatteryHelper = mBatteryStatsHelper;
- mController.mSipper = mBatterySipper;
mController.mBatteryUsageStats = mBatteryUsageStats;
mController.mUidBatteryConsumer = mUidBatteryConsumer;
@@ -183,8 +166,6 @@
when(mFragment.getActivity()).thenReturn(mActivity);
final String key = mController.getPreferenceKey();
when(mBatteryPreference.getKey()).thenReturn(key);
- mController.mSipper = mBatterySipper;
- mController.mBatteryHelper = mBatteryStatsHelper;
mController.mBatteryUsageStats = mBatteryUsageStats;
mController.mUidBatteryConsumer = mUidBatteryConsumer;
@@ -199,8 +180,8 @@
mController.onResume();
verify(mLoaderManager)
- .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY, Bundle.EMPTY,
- mController.mBatteryStatsHelperLoaderCallbacks);
+ .restartLoader(AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS, Bundle.EMPTY,
+ mController.mBatteryUsageStatsLoaderCallbacks);
}
@Test
@@ -209,6 +190,6 @@
mController.onPause();
- verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY);
+ verify(mLoaderManager).destroyLoader(AppInfoDashboardFragment.LOADER_BATTERY_USAGE_STATS);
}
}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
index 1f513a3..d5e5080 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppVersionPreferenceControllerTest.java
@@ -16,6 +16,8 @@
package com.android.settings.applications.appinfo;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -62,4 +64,13 @@
verify(mPreference).setSummary("version test1234");
}
+
+ @Test
+ public void updateState_packageInfoNull_shouldNotCrash() {
+ when(mFragment.getPackageInfo()).thenReturn(null);
+
+ mController.updateState(mPreference);
+
+ assertThat(mController.getSummary()).isNull();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/MediaManagementAppsDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/MediaManagementAppsDetailsTest.java
new file mode 100644
index 0000000..6297064
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/MediaManagementAppsDetailsTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.appinfo;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.applications.AppStateAppOpsBridge;
+import com.android.settings.applications.AppStateMediaManagementAppsBridge;
+
+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.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public class MediaManagementAppsDetailsTest {
+
+ @Mock
+ private SwitchPreference mSwitchPref;
+ @Mock
+ private PackageInfo mPackageInfo;
+ @Mock
+ private AppStateMediaManagementAppsBridge mAppStateBridge;
+ @Mock
+ private AppStateAppOpsBridge.PermissionState mPermissionState;
+
+ private MediaManagementAppsDetails mFragment = new MediaManagementAppsDetails();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ ReflectionHelpers.setField(mFragment, "mSwitchPref", mSwitchPref);
+ ReflectionHelpers.setField(mFragment, "mAppBridge", mAppStateBridge);
+ }
+
+ @Test
+ public void refreshUi_noPackageInfo_returnFalse() {
+ assertThat(mFragment.refreshUi()).isFalse();
+ }
+
+ @Test
+ public void refreshUi_noApplicationInfo_returnFalse() {
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+
+ assertThat(mFragment.refreshUi()).isFalse();
+ }
+
+ @Test
+ public void refreshUi_hasApplicationInfo_returnTrue() {
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+ mPackageInfo.applicationInfo = new ApplicationInfo();
+ when(mAppStateBridge.createPermissionState(nullable(String.class), anyInt()))
+ .thenReturn(mPermissionState);
+
+ assertThat(mFragment.refreshUi()).isTrue();
+ }
+
+ @Test
+ public void refreshUi_permissionDeclaredFalse_switchPreferenceUnEnabled() {
+ assert_refreshUi_switchPreferenceSetEnabled(false /* isPermissionDeclared */);
+ }
+
+ @Test
+ public void refreshUi_permissionDeclaredTrue_switchPreferenceEnabled() {
+ assert_refreshUi_switchPreferenceSetEnabled(true /* isPermissionDeclared */);
+ }
+
+ @Test
+ public void refreshUi_isPermissibleFalse_switchPreferenceUnChecked() {
+ assert_refreshUi_switchPreferenceSetChecked(false /* isPermissible */);
+ }
+
+ @Test
+ public void refreshUi_isPermissibleTrue_switchPreferenceChecked() {
+ assert_refreshUi_switchPreferenceSetChecked(true /* isPermissible */);
+ }
+
+ private void assert_refreshUi_switchPreferenceSetEnabled(boolean isPermissionDeclared) {
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+ mPackageInfo.applicationInfo = new ApplicationInfo();
+ when(mAppStateBridge.createPermissionState(nullable(String.class), anyInt()))
+ .thenReturn(mPermissionState);
+ mPermissionState.permissionDeclared = isPermissionDeclared;
+
+ mFragment.refreshUi();
+ verify(mSwitchPref).setEnabled(isPermissionDeclared);
+ }
+
+ private void assert_refreshUi_switchPreferenceSetChecked(boolean isPermissible) {
+ ReflectionHelpers.setField(mFragment, "mPackageInfo", mPackageInfo);
+ mPackageInfo.applicationInfo = new ApplicationInfo();
+ when(mAppStateBridge.createPermissionState(nullable(String.class), anyInt()))
+ .thenReturn(mPermissionState);
+ when(mPermissionState.isPermissible()).thenReturn(isPermissible);
+
+ mFragment.refreshUi();
+ verify(mSwitchPref).setChecked(isPermissible);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
index 31c00ec..e78a394 100644
--- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceControllerTest.java
@@ -16,7 +16,7 @@
package com.android.settings.applications.defaultapps;
-import static com.android.settingslib.TwoTargetPreference.ICON_SIZE_MEDIUM;
+import static com.android.settingslib.widget.TwoTargetPreference.ICON_SIZE_MEDIUM;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -28,8 +28,8 @@
import androidx.preference.Preference;
import com.android.settings.R;
-import com.android.settingslib.TwoTargetPreference;
import com.android.settingslib.applications.DefaultAppInfo;
+import com.android.settingslib.widget.TwoTargetPreference;
import org.junit.Before;
import org.junit.Test;
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSuggestionActivityTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSuggestionActivityTest.java
index d3a1fc1..bf1d256 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSuggestionActivityTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSuggestionActivityTest.java
@@ -25,6 +25,7 @@
import android.app.KeyguardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -53,6 +54,7 @@
import org.robolectric.shadows.ShadowKeyguardManager;
import java.util.ArrayList;
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
@@ -70,10 +72,19 @@
Shadows.shadowOf(application.getPackageManager())
.setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true);
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_REAR,
true /* resetLockoutRequiresHardwareAuthToken */);
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroductionTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroductionTest.java
index 25780f7..f8517ba 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroductionTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/SetupFingerprintEnrollIntroductionTest.java
@@ -24,8 +24,8 @@
import android.app.KeyguardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
-import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.view.View;
@@ -59,6 +59,7 @@
import org.robolectric.shadows.ShadowKeyguardManager;
import java.util.ArrayList;
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
@@ -76,10 +77,19 @@
Shadows.shadowOf(application.getPackageManager())
.setSystemFeature(PackageManager.FEATURE_FINGERPRINT, true);
+ final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
+ componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */));
+ componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */));
+
final FingerprintSensorPropertiesInternal prop = new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
5 /* maxEnrollmentsPerUser */,
+ componentInfo,
FingerprintSensorProperties.TYPE_REAR,
true /* resetLockoutRequiresHardwareAuthToken */);
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
diff --git a/tests/robotests/src/com/android/settings/datausage/BillingCycleSettingsTest.java b/tests/robotests/src/com/android/settings/datausage/BillingCycleSettingsTest.java
index 3fdb7b4..861b4e3 100644
--- a/tests/robotests/src/com/android/settings/datausage/BillingCycleSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/BillingCycleSettingsTest.java
@@ -36,8 +36,8 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.net.ConnectivityManager;
import android.net.NetworkPolicyManager;
import android.os.Bundle;
@@ -72,9 +72,9 @@
@Mock
private NetworkPolicyEditor mNetworkPolicyEditor;
@Mock
- private ConnectivityManager mConnectivityManager;
- @Mock
private NetworkPolicyManager mNetworkPolicyManager;
+ @Mock
+ private PackageManager mMockPackageManager;
private Context mContext;
@Mock
@@ -157,9 +157,8 @@
.onCreatePreferences(any(Bundle.class), nullable(String.class));
when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
.thenReturn(mNetworkPolicyManager);
- when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
- .thenReturn(mConnectivityManager);
- when(mConnectivityManager.isNetworkSupported(anyInt())).thenReturn(true);
+ when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockPackageManager.hasSystemFeature(any())).thenReturn(true);
final SwitchPreference preference = mock(SwitchPreference.class);
when(billingCycleSettings.findPreference(anyString())).thenReturn(preference);
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
index 4a5bc70..6a7f237 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageSummaryPreferenceControllerTest.java
@@ -16,13 +16,14 @@
package com.android.settings.datausage;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -31,7 +32,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
+import android.content.pm.PackageManager;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -104,7 +105,7 @@
@Mock
private TelephonyManager mTelephonyManager;
@Mock
- private ConnectivityManager mConnectivityManager;
+ private PackageManager mPm;
private DataUsageInfoController mDataInfoController;
@@ -138,10 +139,9 @@
doReturn(mTelephonyManager).when(mActivity).getSystemService(TelephonyManager.class);
doReturn(mTelephonyManager).when(mTelephonyManager)
.createForSubscriptionId(mDefaultSubscriptionId);
- when(mActivity.getSystemService(Context.CONNECTIVITY_SERVICE))
- .thenReturn(mConnectivityManager);
+ doReturn(mPm).when(mActivity).getPackageManager();
+ doReturn(false).when(mPm).hasSystemFeature(eq(FEATURE_WIFI));
doReturn(TelephonyManager.SIM_STATE_READY).when(mTelephonyManager).getSimState();
- when(mConnectivityManager.isNetworkSupported(TYPE_WIFI)).thenReturn(false);
mController = spy(new DataUsageSummaryPreferenceController(
mDataUsageController,
@@ -363,7 +363,7 @@
final int subscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mController.init(subscriptionId);
mController.mDataUsageController = mDataUsageController;
- when(mConnectivityManager.isNetworkSupported(TYPE_WIFI)).thenReturn(true);
+ doReturn(true).when(mPm).hasSystemFeature(eq(FEATURE_WIFI));
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageUtilsTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageUtilsTest.java
index 21f9d1a..a465d74 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageUtilsTest.java
@@ -18,14 +18,15 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.robolectric.Shadows.shadowOf;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.telephony.TelephonyManager;
import android.util.DataUnit;
@@ -38,13 +39,12 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
public final class DataUsageUtilsTest {
@Mock
- private ConnectivityManager mManager;
- @Mock
private TelephonyManager mTelephonyManager;
@Mock
private NetworkStatsManager mNetworkStatsManager;
@@ -56,21 +56,20 @@
MockitoAnnotations.initMocks(this);
final ShadowApplication shadowContext = ShadowApplication.getInstance();
mContext = RuntimeEnvironment.application;
- shadowContext.setSystemService(Context.CONNECTIVITY_SERVICE, mManager);
shadowContext.setSystemService(Context.TELEPHONY_SERVICE, mTelephonyManager);
shadowContext.setSystemService(Context.NETWORK_STATS_SERVICE, mNetworkStatsManager);
}
@Test
public void mobileDataStatus_whenNetworkIsSupported() {
- when(mManager.isNetworkSupported(anyInt())).thenReturn(true);
+ when(mTelephonyManager.isDataCapable()).thenReturn(true);
final boolean hasMobileData = DataUsageUtils.hasMobileData(mContext);
assertThat(hasMobileData).isTrue();
}
@Test
public void mobileDataStatus_whenNetworkIsNotSupported() {
- when(mManager.isNetworkSupported(anyInt())).thenReturn(false);
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
final boolean hasMobileData = DataUsageUtils.hasMobileData(mContext);
assertThat(hasMobileData).isFalse();
}
@@ -85,7 +84,8 @@
@Test
public void hasEthernet_shouldQueryEthernetSummaryForUser() throws Exception {
- when(mManager.isNetworkSupported(anyInt())).thenReturn(true);
+ ShadowPackageManager pm = shadowOf(RuntimeEnvironment.application.getPackageManager());
+ pm.setSystemFeature(PackageManager.FEATURE_ETHERNET, true);
final String subscriber = "TestSub";
when(mTelephonyManager.getSubscriberId()).thenReturn(subscriber);
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
index 8a68f38..eb29b7c 100644
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
@@ -18,17 +18,16 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.robolectric.shadow.api.Shadow.extract;
+import static org.mockito.Mockito.when;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.provider.Settings;
+import android.telephony.TelephonyManager;
import androidx.preference.Preference;
-import com.android.settings.testutils.shadow.ShadowConnectivityManager;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,27 +35,28 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowConnectivityManager.class)
public class AutoTimeZonePreferenceControllerTest {
@Mock
private UpdateTimeAndDateCallback mCallback;
-
+ @Mock
private Context mContext;
private AutoTimeZonePreferenceController mController;
private Preference mPreference;
- private ShadowConnectivityManager connectivityManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = spy(RuntimeEnvironment.application);
+
mPreference = new Preference(mContext);
- connectivityManager = extract(mContext.getSystemService(ConnectivityManager.class));
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true);
+
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.isDataCapable()).thenReturn(true);
}
@Test
@@ -77,8 +77,7 @@
@Test
public void isWifiOnly_notAvailable() {
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
-
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
mController = new AutoTimeZonePreferenceController(
mContext, null /* callback */, false /* fromSUW */);
@@ -95,8 +94,7 @@
@Test
public void isWifiOnly_notEnable() {
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
-
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
mController = new AutoTimeZonePreferenceController(
mContext, null /* callback */, false /* fromSUW */);
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
index ae200b9..68b2990 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
@@ -16,9 +16,9 @@
package com.android.settings.datetime;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.time.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.app.time.Capabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.time.Capabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.time.Capabilities.CAPABILITY_POSSESSED;
import static com.google.common.truth.Truth.assertThat;
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.time.Capabilities;
import android.app.time.TimeManager;
import android.app.time.TimeZoneCapabilities;
import android.app.time.TimeZoneCapabilitiesAndConfig;
@@ -142,7 +143,7 @@
}
private static TimeZoneCapabilities createTimeZoneCapabilities(
- @TimeZoneCapabilities.CapabilityState int geoDetectionCapability) {
+ @Capabilities.CapabilityState int geoDetectionCapability) {
UserHandle arbitraryUserHandle = UserHandle.of(123);
return new TimeZoneCapabilities.Builder(arbitraryUserHandle)
.setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java
deleted file mode 100644
index ed7f16b..0000000
--- a/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.deviceinfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-import android.os.storage.VolumeInfo;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.shadows.ShadowApplication;
-
-@RunWith(RobolectricTestRunner.class)
-public class PrivateVolumeOptionMenuControllerTest {
-
- @Mock
- private MenuItem mMigrateMenuItem;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Menu mMenu;
- @Mock
- private MenuInflater mMenuInflater;
- @Mock
- private PackageManager mPm;
- @Mock
- private VolumeInfo mVolumeInfo;
- @Mock
- private VolumeInfo mPrimaryInfo;
-
- private PrivateVolumeOptionMenuController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
- when(mVolumeInfo.isMountedWritable()).thenReturn(true);
- when(mPrimaryInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
- when(mMenu.findItem(anyInt())).thenReturn(mMigrateMenuItem);
- when(mMigrateMenuItem.getItemId()).thenReturn(100);
-
- mController = new PrivateVolumeOptionMenuController(
- Robolectric.setupActivity(Activity.class), mPrimaryInfo, mPm);
- }
-
- @Test
- public void testMigrateDataMenuItemIsAdded() {
- mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-
- verify(mMenu).add(Menu.NONE, 100, Menu.NONE, R.string.storage_menu_migrate);
- }
-
- @Test
- public void testMigrateDataIsNotVisibleNormally() {
- when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mPrimaryInfo);
- when(mPrimaryInfo.isMountedWritable()).thenReturn(true);
-
- mController.onCreateOptionsMenu(mMenu, mMenuInflater);
- mController.onPrepareOptionsMenu(mMenu);
-
- verify(mMigrateMenuItem).setVisible(false);
- }
-
- @Test
- public void testMigrateDataIsVisibleWhenExternalVolumeIsPrimary() {
- when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
-
- mController.onCreateOptionsMenu(mMenu, mMenuInflater);
- mController.onPrepareOptionsMenu(mMenu);
-
- verify(mMigrateMenuItem).setVisible(true);
- }
-
- @Test
- public void testMigrateDataIsNotVisibleWhenExternalVolumeIsNotMounted() {
- when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
- when(mVolumeInfo.isMountedWritable()).thenReturn(false);
-
- mController.onCreateOptionsMenu(mMenu, mMenuInflater);
- mController.onPrepareOptionsMenu(mMenu);
-
- verify(mMigrateMenuItem).setVisible(false);
- }
-
- @Test
- public void testMigrateDataGoesToMigrateWizard() {
- when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
-
- mController.onCreateOptionsMenu(mMenu, mMenuInflater);
- mController.onPrepareOptionsMenu(mMenu);
-
- assertThat(mController.onOptionsItemSelected(mMigrateMenuItem)).isTrue();
- ShadowApplication shadowApplication = ShadowApplication.getInstance();
- assertThat(shadowApplication).isNotNull();
- assertThat(shadowApplication.getNextStartedActivity().getComponent().getClassName())
- .isEqualTo(StorageWizardMigrateConfirm.class.getName());
- }
-}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceControllerTest.java
index 1b9c487..464d9a2 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/RegulatoryInfoPreferenceControllerTest.java
@@ -35,10 +35,10 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
-import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class RegulatoryInfoPreferenceControllerTest {
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
index aad3f30..d96473b 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageSettingsTest.java
@@ -34,10 +34,10 @@
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
import java.util.List;
-import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class StorageSettingsTest {
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java
index 8230144..87fdb22 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/firmwareversion/BasebandVersionPreferenceControllerTest.java
@@ -21,45 +21,42 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.robolectric.shadow.api.Shadow.extract;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.sysprop.TelephonyProperties;
-
-import com.android.settings.testutils.shadow.ShadowConnectivityManager;
+import android.telephony.TelephonyManager;
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.Arrays;
-
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowConnectivityManager.class)
public class BasebandVersionPreferenceControllerTest {
-
+ @Mock
private Context mContext;
private BasebandVersionPreferenceController mController;
+ @Mock
+ private TelephonyManager mTelephonyManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
+ mContext = spy(RuntimeEnvironment.application);
mController = new BasebandVersionPreferenceController(mContext, "key");
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
}
@Test
public void getAvailability_wifiOnly_unavailable() {
- final ShadowConnectivityManager connectivityManager =
- extract(mContext.getSystemService(ConnectivityManager.class));
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, false);
-
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@@ -67,10 +64,7 @@
public void getAvailability_hasMobile_available() {
final String text = "test";
TelephonyProperties.baseband_version(Arrays.asList(new String[]{text}));
- ShadowConnectivityManager connectivityManager =
- extract(mContext.getSystemService(ConnectivityManager.class));
- connectivityManager.setNetworkSupported(ConnectivityManager.TYPE_MOBILE, true);
-
+ when(mTelephonyManager.isDataCapable()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
}
diff --git a/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java
index 7b3ae65..6ad9974 100644
--- a/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/TopLevelWallpaperPreferenceControllerTest.java
@@ -202,4 +202,20 @@
assertThat(Shadows.shadowOf(mContext).getNextStartedActivityForResult()
.intent.hasExtra("com.android.wallpaper.LAUNCH_SOURCE")).isTrue();
}
+
+ @Test
+ public void handlePreferenceTreeClick_launchClearTask() {
+ mShadowPackageManager.setResolveInfosForIntent(
+ mWallpaperIntent, Lists.newArrayList());
+ mShadowPackageManager.setResolveInfosForIntent(
+ mStylesAndWallpaperIntent, Lists.newArrayList(mock(ResolveInfo.class)));
+
+ Preference preference = new Preference(mContext);
+ preference.setKey(TEST_KEY);
+
+ mController.handlePreferenceTreeClick(preference);
+
+ assertThat((Shadows.shadowOf(mContext).getNextStartedActivityForResult()
+ .intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_TASK) != 0).isTrue();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java
index b245017..5c3dacd 100644
--- a/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/ActionDisabledByAdminDialogHelperTest.java
@@ -36,10 +36,8 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
@@ -109,20 +107,6 @@
}
@Test
- public void testSetAdminSupportIconForFinancedDevice_adminSupportIconIsGone() {
- final ShadowDevicePolicyManager dpmShadow = ShadowDevicePolicyManager.getShadow();
- final ViewGroup view = new FrameLayout(mActivity);
- final ImageView supportIconImageView = createAdminSupportIconImageView(view, mActivity);
- final ComponentName component = new ComponentName("some.package.name",
- "some.package.name.SomeClass");
- setupFinancedDevice(dpmShadow);
-
- mHelper.setAdminSupportIcon(view, component, 123);
-
- assertEquals(View.GONE, supportIconImageView.getVisibility());
- }
-
- @Test
public void testSetAdminSupportTitle() {
final ViewGroup view = new FrameLayout(mActivity);
final TextView textView = createAdminSupportDialogTitleTextView(view, mActivity);
@@ -260,14 +244,6 @@
verify(builder, never()).setNeutralButton(anyInt(), any());
}
- private static ImageView createAdminSupportIconImageView(final ViewGroup view,
- final Activity activity) {
- final ImageView supportIconView = new ImageView(activity);
- supportIconView.setId(R.id.admin_support_icon);
- view.addView(supportIconView);
- return supportIconView;
- }
-
private static TextView createAdminSupportDialogTitleTextView(final ViewGroup view,
final Activity activity) {
final TextView textView = new TextView(activity);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index 8eeac8d..7f76c70 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -47,8 +47,6 @@
import androidx.preference.Preference;
import androidx.recyclerview.widget.RecyclerView;
-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.testutils.FakeFeatureFactory;
@@ -114,12 +112,6 @@
@Mock
private BatteryEntry mBatteryEntry;
@Mock
- private BatterySipper mBatterySipper;
- @Mock
- private BatteryStatsHelper mBatteryStatsHelper;
- @Mock
- private BatteryStats.Uid mUid;
- @Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@@ -140,6 +132,7 @@
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getPackageName()).thenReturn("foo");
FakeFeatureFactory.setupForTest();
mFragment = spy(new AdvancedPowerUsageDetail());
@@ -168,19 +161,11 @@
doReturn(mEntityHeaderController).when(mEntityHeaderController)
.setSummary(nullable(String.class));
- doReturn(UID).when(mBatterySipper).getUid();
+ when(mBatteryEntry.getUid()).thenReturn(UID);
when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL);
- doReturn(BACKGROUND_TIME_US).when(mUid).getProcessStateTime(
- eq(BatteryStats.Uid.PROCESS_STATE_BACKGROUND), anyLong(), anyInt());
- doReturn(PROCSTATE_TOP_TIME_US).when(mUid).getProcessStateTime(
- eq(BatteryStats.Uid.PROCESS_STATE_TOP), anyLong(), anyInt());
- doReturn(mForegroundActivityTimer).when(mUid).getForegroundActivityTimer();
- doReturn(FOREGROUND_ACTIVITY_TIME_US).when(mForegroundActivityTimer)
- .getTotalTimeLocked(anyLong(), anyInt());
- ReflectionHelpers.setField(mBatteryEntry, "sipper", mBatterySipper);
+ when(mBatteryEntry.getTimeInBackgroundMs()).thenReturn(BACKGROUND_TIME_MS);
+ when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(FOREGROUND_TIME_MS);
mBatteryEntry.iconId = ICON_ID;
- mBatterySipper.uidObj = mUid;
- mBatterySipper.drainType = BatterySipper.DrainType.APP;
mFragment.mHeaderPreference = mHeaderPreference;
mFragment.mState = mState;
@@ -200,6 +185,7 @@
Answer<Void> callable = invocation -> {
mBundle = captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ System.out.println("mBundle = " + mBundle);
return null;
};
doAnswer(callable).when(mActivity).startActivityAsUser(captor.capture(),
@@ -262,8 +248,8 @@
@Test
public void testStartBatteryDetailPage_hasBasicData() {
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
+ mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME))
@@ -275,28 +261,11 @@
}
@Test
- public void testStartBatteryDetailPage_typeNotApp_hasBasicData() {
- mBatterySipper.drainType = BatterySipper.DrainType.PHONE;
- mBatterySipper.usageTimeMs = PHONE_FOREGROUND_TIME_MS;
-
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
-
- assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
- assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME))
- .isEqualTo(PHONE_FOREGROUND_TIME_MS);
- assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME))
- .isEqualTo(PHONE_BACKGROUND_TIME_MS);
- assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
- .isEqualTo(USAGE_PERCENT);
- }
-
- @Test
public void testStartBatteryDetailPage_NormalApp() {
- mBatterySipper.mPackages = PACKAGE_NAME;
- mBatteryEntry.defaultPackageName = PACKAGE_NAME[0];
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
+ when(mBatteryEntry.getDefaultPackageName()).thenReturn(PACKAGE_NAME[0]);
+
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
+ mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)).isEqualTo(
PACKAGE_NAME[0]);
@@ -304,9 +273,10 @@
@Test
public void testStartBatteryDetailPage_SystemApp() {
- mBatterySipper.mPackages = null;
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
+ when(mBatteryEntry.getDefaultPackageName()).thenReturn(null);
+
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
+ mBatteryEntry, USAGE_PERCENT);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_LABEL)).isEqualTo(APP_LABEL);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_ICON_ID)).isEqualTo(ICON_ID);
@@ -316,23 +286,22 @@
@Test
public void testStartBatteryDetailPage_WorkApp() {
final int appUid = 1010019;
- mBatterySipper.mPackages = PACKAGE_NAME;
- doReturn(appUid).when(mBatterySipper).getUid();
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
+ doReturn(appUid).when(mBatteryEntry).getUid();
+
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
+ mBatteryEntry, USAGE_PERCENT);
verify(mActivity).startActivityAsUser(any(Intent.class), eq(new UserHandle(10)));
}
@Test
public void testStartBatteryDetailPage_typeUser_startByCurrentUser() {
- mBatterySipper.drainType = BatterySipper.DrainType.USER;
- mBatterySipper.userId = 10;
+ when(mBatteryEntry.isUserEntry()).thenReturn(true);
final int currentUser = 20;
ShadowActivityManager.setCurrentUser(currentUser);
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
+ AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
+ mBatteryEntry, USAGE_PERCENT);
verify(mActivity).startActivityAsUser(any(Intent.class), eq(new UserHandle(currentUser)));
}
@@ -365,18 +334,6 @@
}
@Test
- public void testStartBatteryDetailPage_defaultPackageNull_chooseFromBatterySipper() {
- mBatteryEntry.defaultPackageName = null;
- mBatteryEntry.sipper.mPackages = PACKAGE_NAME;
-
- AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mBatteryUtils, mFragment,
- mBatteryStatsHelper, 0, mBatteryEntry, USAGE_PERCENT);
-
- assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME))
- .isEqualTo(PACKAGE_NAME[0]);
- }
-
- @Test
public void testInitPreference_hasCorrectSummary() {
Bundle bundle = new Bundle(4);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, BACKGROUND_TIME_MS);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
index 28655f3..1faa75f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryAppListPreferenceControllerTest.java
@@ -20,21 +20,16 @@
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.BatteryStats;
import android.os.UserManager;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import androidx.preference.PreferenceGroup;
-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.core.InstrumentedPreferenceFragment;
@@ -51,13 +46,9 @@
@RunWith(RobolectricTestRunner.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;
@@ -69,6 +60,8 @@
private PackageManager mPackageManager;
@Mock
private UserManager mUserManager;
+ @Mock
+ private BatteryEntry mBatteryEntry;
private Context mContext;
private PowerGaugePreference mPreference;
@@ -87,138 +80,68 @@
FakeFeatureFactory.setupForTest();
mPreference = new PowerGaugePreference(mContext);
- when(mNormalBatterySipper.getPackages()).thenReturn(PACKAGE_NAMES);
- when(mNormalBatterySipper.getUid()).thenReturn(UID);
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- mNormalBatterySipper.uidObj = mock(BatteryStats.Uid.class);
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()));
+ BatteryAppListPreferenceController.sConfig =
+ new BatteryAppListPreferenceController.Config() {
+ @Override
+ public boolean shouldShowBatteryAttributionList(Context context) {
+ return true;
+ }
+ };
}
@Test
public void testSetUsageSummary_timeLessThanOneMinute_DoNotSetSummary() {
- mNormalBatterySipper.usageTimeMs = 59 * DateUtils.SECOND_IN_MILLIS;
+ when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(59 * DateUtils.SECOND_IN_MILLIS);
- mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+ mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
assertThat(mPreference.getSummary()).isNull();
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary() {
- mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
+ when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(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);
+ mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
assertThat(mPreference.getSummary().toString()).isEqualTo("Used for 2 min");
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_GoogleApp_shouldNotSetScreenSummary() {
- mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
- mNormalBatterySipper.packageWithHighestDrain = "com.google.android.googlequicksearchbox";
+ when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(2 * DateUtils.MINUTE_IN_MILLIS);
+ when(mBatteryEntry.getDefaultPackageName())
+ .thenReturn("com.google.android.googlequicksearchbox");
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);
+ mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
assertThat(mPreference.getSummary()).isNull();
}
@Test
public void testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary() {
- mNormalBatterySipper.usageTimeMs = 2 * DateUtils.MINUTE_IN_MILLIS;
- doReturn(true).when(mBatteryUtils).shouldHideSipper(mNormalBatterySipper);
+ when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(2 * DateUtils.MINUTE_IN_MILLIS);
+ when(mBatteryEntry.isHidden()).thenReturn(true);
+
doReturn(mContext).when(mFragment).getContext();
- mPreferenceController.setUsageSummary(mPreference, mNormalBatterySipper);
+ mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
assertThat(mPreference.getSummary().toString()).isEqualTo("2 min");
}
@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("2 min");
- }
-
- @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 testShouldHideSipper_hiddenSystemModule_returnTrue() {
- when(mBatteryUtils.isHiddenSystemModule(mNormalBatterySipper)).thenReturn(true);
-
- assertThat(mPreferenceController.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
public void testNeverUseFakeData() {
assertThat(BatteryAppListPreferenceController.USE_FAKE_DATA).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index a072988..5f08698 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -78,7 +78,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void testOnReceive_batteryLevelChanged_dataUpdated() {
@@ -93,7 +92,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void testOnReceive_batteryHealthChanged_dataUpdated() {
@@ -108,7 +106,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void onReceive_batteryNotPresent_shouldShowHelpMessage() {
@@ -121,7 +118,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void testOnReceive_powerSaveModeChanged_listenerInvoked() {
@@ -133,7 +129,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
@@ -154,7 +149,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void testRegister_updateBatteryStatus() {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java
index e40b270..6858579 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryEntryTest.java
@@ -17,6 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -24,18 +27,21 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.BatteryConsumer;
import android.os.Handler;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
+import android.os.UidBatteryConsumer;
+import android.os.UserBatteryConsumer;
import android.os.UserManager;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatterySipper.DrainType;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -50,121 +56,153 @@
private static final int APP_UID = 123;
private static final int SYSTEM_UID = Process.SYSTEM_UID;
private static final String APP_DEFAULT_PACKAGE_NAME = "com.android.test";
- private static final String APP_LABEL = "Test App Name";
+ private static final String LABEL_PREFIX = "Label for ";
private static final String HIGH_DRAIN_PACKAGE = "com.android.test.screen";
private static final String ANDROID_PACKAGE = "android";
- private static final String[] SYSTEM_PACKAGES = {HIGH_DRAIN_PACKAGE, ANDROID_PACKAGE};
@Rule public MockitoRule mocks = MockitoJUnit.rule();
- @Mock private Context mockContext;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mMockContext;
@Mock private Handler mockHandler;
@Mock private PackageManager mockPackageManager;
@Mock private UserManager mockUserManager;
+ @Mock private UidBatteryConsumer mUidBatteryConsumer;
+ @Mock private SystemBatteryConsumer mSystemBatteryConsumer;
@Before
public void stubContextToReturnMockPackageManager() {
- when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
+ when(mMockContext.getPackageManager()).thenReturn(mockPackageManager);
}
@Before
public void stubPackageManagerToReturnAppPackageAndName() throws NameNotFoundException {
- when(mockPackageManager.getPackagesForUid(APP_UID))
- .thenReturn(new String[] {APP_DEFAULT_PACKAGE_NAME});
-
- ApplicationInfo appInfo = mock(ApplicationInfo.class);
- when(mockPackageManager.getApplicationInfo(APP_DEFAULT_PACKAGE_NAME, 0 /* no flags */))
- .thenReturn(appInfo);
- when(mockPackageManager.getApplicationLabel(appInfo)).thenReturn(APP_LABEL);
+ when(mockPackageManager.getApplicationInfo(anyString(), eq(0) /* no flags */))
+ .thenAnswer(invocation -> {
+ ApplicationInfo info = new ApplicationInfo();
+ info.packageName = invocation.getArgument(0);
+ return info;
+ });
+ when(mockPackageManager.getApplicationLabel(any(ApplicationInfo.class)))
+ .thenAnswer(invocation -> LABEL_PREFIX
+ + ((ApplicationInfo) invocation.getArgument(0)).packageName);
}
- private BatteryEntry createBatteryEntryForApp() {
- return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForApp(),
- null);
+ private BatteryEntry createBatteryEntryForApp(String[] packages, String packageName,
+ String highDrainPackage) {
+ UidBatteryConsumer consumer = mock(UidBatteryConsumer.class);
+ when(consumer.getUid()).thenReturn(APP_UID);
+ when(consumer.getPackageWithHighestDrain()).thenReturn(highDrainPackage);
+ return new BatteryEntry(mMockContext, mockHandler, mockUserManager,
+ consumer, false, packages, packageName);
}
- private BatterySipper createSipperForApp() {
- BatterySipper sipper =
- new BatterySipper(DrainType.APP, new FakeUid(APP_UID), 0 /* power use */);
- sipper.packageWithHighestDrain = HIGH_DRAIN_PACKAGE;
- return sipper;
+ private BatteryEntry createSystemBatteryEntry(int drainType) {
+ SystemBatteryConsumer consumer = mock(SystemBatteryConsumer.class);
+ when(consumer.getDrainType()).thenReturn(drainType);
+ return new BatteryEntry(mMockContext, mockHandler, mockUserManager,
+ consumer, false, null, null);
}
- private BatteryEntry createBatteryEntryForSystem() {
- return new BatteryEntry(mockContext, mockHandler, mockUserManager, createSipperForSystem(),
- null);
- }
-
- private BatterySipper createSipperForSystem() {
- BatterySipper sipper =
- new BatterySipper(DrainType.APP, new FakeUid(SYSTEM_UID), 0 /* power use */);
- sipper.packageWithHighestDrain = HIGH_DRAIN_PACKAGE;
- sipper.mPackages = SYSTEM_PACKAGES;
- return sipper;
+ private BatteryEntry createUserBatteryConsumer(int userId) {
+ UserBatteryConsumer consumer = mock(UserBatteryConsumer.class);
+ when(consumer.getUserId()).thenReturn(userId);
+ return new BatteryEntry(mMockContext, mockHandler, mockUserManager,
+ consumer, false, null, null);
}
@Test
public void batteryEntryForApp_shouldSetDefaultPackageNameAndLabel() throws Exception {
- BatteryEntry entry = createBatteryEntryForApp();
+ BatteryEntry entry = createBatteryEntryForApp(null, APP_DEFAULT_PACKAGE_NAME,
+ HIGH_DRAIN_PACKAGE);
- assertThat(entry.defaultPackageName).isEqualTo(APP_DEFAULT_PACKAGE_NAME);
- assertThat(entry.getLabel()).isEqualTo(APP_LABEL);
+ assertThat(entry.getDefaultPackageName()).isEqualTo(APP_DEFAULT_PACKAGE_NAME);
+ assertThat(entry.getLabel()).isEqualTo(LABEL_PREFIX + APP_DEFAULT_PACKAGE_NAME);
}
@Test
public void batteryEntryForApp_shouldSetLabelAsPackageName_whenPackageCannotBeFound()
- throws Exception {
- when(mockPackageManager.getApplicationInfo(APP_DEFAULT_PACKAGE_NAME, 0 /* no flags */))
- .thenThrow(new NameNotFoundException());
+ throws Exception {
+ when(mockPackageManager.getApplicationInfo(APP_DEFAULT_PACKAGE_NAME, 0 /* no flags */))
+ .thenThrow(new NameNotFoundException());
- BatteryEntry entry = createBatteryEntryForApp();
+ BatteryEntry entry = createBatteryEntryForApp(null, APP_DEFAULT_PACKAGE_NAME, null);
- assertThat(entry.getLabel()).isEqualTo(APP_DEFAULT_PACKAGE_NAME);
+ assertThat(entry.getLabel()).isEqualTo(APP_DEFAULT_PACKAGE_NAME);
}
@Test
public void batteryEntryForApp_shouldSetHighestDrainPackage_whenPackagesCannotBeFoundForUid() {
when(mockPackageManager.getPackagesForUid(APP_UID)).thenReturn(null);
- BatteryEntry entry = createBatteryEntryForApp();
+ BatteryEntry entry = createBatteryEntryForApp(null, null, HIGH_DRAIN_PACKAGE);
- assertThat(entry.getLabel()).isEqualTo(HIGH_DRAIN_PACKAGE);
+ assertThat(entry.getLabel()).isEqualTo(LABEL_PREFIX + HIGH_DRAIN_PACKAGE);
}
@Test
public void batteryEntryForApp_shouldSetHighestDrainPackage_whenMultiplePackagesFoundForUid() {
- when(mockPackageManager.getPackagesForUid(APP_UID))
- .thenReturn(new String[] {APP_DEFAULT_PACKAGE_NAME, "package2", "package3"});
+ BatteryEntry entry = createBatteryEntryForApp(
+ new String[] {APP_DEFAULT_PACKAGE_NAME, "package2", "package3"}, null,
+ HIGH_DRAIN_PACKAGE);
- BatteryEntry entry = createBatteryEntryForApp();
-
- assertThat(entry.getLabel()).isEqualTo(HIGH_DRAIN_PACKAGE);
+ assertThat(entry.getLabel()).isEqualTo(LABEL_PREFIX + HIGH_DRAIN_PACKAGE);
}
@Test
public void batteryEntryForAOD_containCorrectInfo() {
- final BatterySipper batterySipper = mock(BatterySipper.class);
- batterySipper.drainType = DrainType.AMBIENT_DISPLAY;
+ final SystemBatteryConsumer systemBatteryConsumer = mock(SystemBatteryConsumer.class);
+ when(systemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_AMBIENT_DISPLAY);
final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler,
- mockUserManager, batterySipper, null);
+ mockUserManager, systemBatteryConsumer, false, null, null);
assertThat(entry.iconId).isEqualTo(R.drawable.ic_settings_aod);
assertThat(entry.name).isEqualTo("Ambient display");
}
@Test
- public void extractPackageFromSipper_systemSipper_returnSystemPackage() {
- BatteryEntry entry = createBatteryEntryForSystem();
+ public void getTimeInForegroundMs_app() {
+ final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler,
+ mockUserManager, mUidBatteryConsumer, false, null, null);
- assertThat(entry.extractPackagesFromSipper(entry.sipper))
- .isEqualTo(new String[] {ANDROID_PACKAGE});
+ when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
+ .thenReturn(100L);
+
+ assertThat(entry.getTimeInForegroundMs()).isEqualTo(100L);
}
@Test
- public void extractPackageFromSipper_normalSipper_returnDefaultPackage() {
- BatteryEntry entry = createBatteryEntryForApp();
+ public void getTimeInForegroundMs_systemConsumer() {
+ final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler,
+ mockUserManager, mSystemBatteryConsumer, false, null, null);
- assertThat(entry.extractPackagesFromSipper(entry.sipper)).isEqualTo(entry.sipper.mPackages);
+ when(mSystemBatteryConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
+ .thenReturn(100L);
+
+ assertThat(entry.getTimeInForegroundMs()).isEqualTo(100L);
+ }
+
+ @Test
+ public void getTimeInBackgroundMs_app() {
+ final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler,
+ mockUserManager, mUidBatteryConsumer, false, null, null);
+
+ when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
+ .thenReturn(100L);
+
+ assertThat(entry.getTimeInBackgroundMs()).isEqualTo(100L);
+ }
+
+ @Test
+ public void getTimeInBackgroundMs_systemConsumer() {
+ final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mockHandler,
+ mockUserManager, mSystemBatteryConsumer, false, null, null);
+
+ when(mSystemBatteryConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE))
+ .thenReturn(100L);
+
+ assertThat(entry.getTimeInBackgroundMs()).isEqualTo(0);
}
@Test
@@ -176,7 +214,29 @@
assertThat(BatteryEntry.sUidCache).isNotEmpty();
Locale.setDefault(new Locale("zh_TW"));
- createBatteryEntryForApp();
+ createBatteryEntryForApp(null, null, HIGH_DRAIN_PACKAGE);
assertThat(BatteryEntry.sUidCache).isEmpty(); // check if cache is clear
}
+
+ @Test
+ public void getKey_UidBatteryConsumer() {
+ final BatteryEntry entry = createBatteryEntryForApp(null, null, null);
+ final String key = entry.getKey();
+ assertThat(key).isEqualTo("123");
+ }
+
+ @Test
+ public void getKey_SystemBatteryConsumer_returnDrainType() {
+ final BatteryEntry entry =
+ createSystemBatteryEntry(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH);
+ final String key = entry.getKey();
+ assertThat(key).isEqualTo("S|2");
+ }
+
+ @Test
+ public void getKey_UserBatteryConsumer_returnUserId() {
+ final BatteryEntry entry = createUserBatteryConsumer(2);
+ final String key = entry.getKey();
+ assertThat(key).isEqualTo("U|2");
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java
deleted file mode 100644
index 92a3dc0..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryStatsHelperLoaderTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-
-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;
-
-@RunWith(RobolectricTestRunner.class)
-public class BatteryStatsHelperLoaderTest {
- @Mock
- private BatteryUtils mBatteryUtils;
- @Mock
- private ConnectivityManager mConnectivityManager;
-
- private Context mContext;
- private BatteryStatsHelperLoader mBatteryStatsHelperLoader;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
- doReturn(mConnectivityManager).when(mContext).getSystemService(
- Context.CONNECTIVITY_SERVICE);
-
- mBatteryStatsHelperLoader = spy(new BatteryStatsHelperLoader(mContext));
- mBatteryStatsHelperLoader.mBatteryUtils = mBatteryUtils;
- }
-
- @Test
- public void testLoadInBackground_loadWithoutBundle() {
- when(mBatteryStatsHelperLoader.getContext()).thenReturn(mContext);
- mBatteryStatsHelperLoader.loadInBackground();
-
- verify(mBatteryUtils).initBatteryStatsHelper(any(), eq(null), any());
- }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java
new file mode 100644
index 0000000..8c47ff6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUsageStatsLoaderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
+import android.os.BatteryUsageStatsQuery;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BatteryUsageStatsLoaderTest {
+ private Context mContext;
+ @Mock
+ private BatteryStatsManager mBatteryStatsManager;
+ @Mock
+ private BatteryUsageStats mBatteryUsageStats;
+ @Captor
+ private ArgumentCaptor<BatteryUsageStatsQuery> mUsageStatsQueryCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mBatteryStatsManager).when(mContext).getSystemService(
+ Context.BATTERY_STATS_SERVICE);
+ }
+
+ @Test
+ public void testLoadInBackground_loadWithoutHistory() {
+ BatteryUsageStatsLoader loader = new BatteryUsageStatsLoader(
+ mContext, /* includeBatteryHistory */ false);
+
+ when(mBatteryStatsManager.getBatteryUsageStats(mUsageStatsQueryCaptor.capture()))
+ .thenReturn(mBatteryUsageStats);
+
+ loader.loadInBackground();
+
+ final int queryFlags = mUsageStatsQueryCaptor.getValue().getFlags();
+ assertThat(queryFlags
+ & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY).isEqualTo(0);
+ }
+
+ @Test
+ public void testLoadInBackground_loadWithHistory() {
+ BatteryUsageStatsLoader loader = new BatteryUsageStatsLoader(
+ mContext, /* includeBatteryHistory */ true);
+
+ when(mBatteryStatsManager.getBatteryUsageStats(mUsageStatsQueryCaptor.capture()))
+ .thenReturn(mBatteryUsageStats);
+
+ loader.loadInBackground();
+
+ final int queryFlags = mUsageStatsQueryCaptor.getValue().getFlags();
+ assertThat(queryFlags
+ & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY).isNotEqualTo(0);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index c8fdf8c..775ca40 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -28,11 +28,8 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -49,15 +46,12 @@
import android.content.pm.ResolveInfo;
import android.os.BatteryStats;
import android.os.BatteryStatsManager;
+import android.os.BatteryUsageStats;
import android.os.Build;
-import android.os.Bundle;
import android.os.Process;
+import android.os.SystemBatteryConsumer;
import android.os.SystemClock;
-import android.os.UserManager;
-import android.text.format.DateUtils;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.batterytip.AnomalyDatabaseHelper;
import com.android.settings.fuelgauge.batterytip.AnomalyInfo;
import com.android.settings.fuelgauge.batterytip.BatteryDatabaseManager;
@@ -91,8 +85,6 @@
private static final long TIME_STATE_TOP_SLEEPING = 2500 * UNIT;
private static final long TIME_STATE_FOREGROUND = 3000 * UNIT;
private static final long TIME_STATE_BACKGROUND = 6000 * UNIT;
- private static final long TIME_FOREGROUND_ZERO = 0;
- private static final long TIME_FOREGROUND = 100 * DateUtils.MINUTE_IN_MILLIS;
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;
@@ -101,18 +93,10 @@
private static final long TIME_EXPECTED_FOREGROUND = 1500;
private static final long TIME_EXPECTED_BACKGROUND = 6000;
private static final long TIME_EXPECTED_ALL = 7500;
- private static final double BATTERY_SCREEN_USAGE = 300;
- private static final double BATTERY_IDLE_USAGE = 600;
private static final double BATTERY_SYSTEM_USAGE = 600;
- private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
- private static final double BATTERY_UNACCOUNTED_USAGE = 700;
- private static final double BATTERY_APP_USAGE = 100;
- private static final double BATTERY_WIFI_USAGE = 200;
- private static final double BATTERY_BLUETOOTH_USAGE = 300;
private static final double TOTAL_BATTERY_USAGE = 1000;
- private static final double HIDDEN_USAGE = 200;
private static final int DISCHARGE_AMOUNT = 80;
- private static final double PERCENT_SYSTEM_USAGE = 60;
+ private static final double PERCENT_SYSTEM_USAGE = 48;
private static final double PRECISION = 0.001;
private static final int SDK_VERSION = Build.VERSION_CODES.L;
private static final String PACKAGE_NAME = "com.android.app";
@@ -127,38 +111,18 @@
@Mock
private BatteryStats.Timer mTimer;
@Mock
- private BatterySipper mNormalBatterySipper;
+ private BatteryUsageStats mBatteryUsageStats;
@Mock
- private BatterySipper mWifiBatterySipper;
- @Mock
- private BatterySipper mBluetoothBatterySipper;
- @Mock
- private BatterySipper mScreenBatterySipper;
- @Mock
- private BatterySipper mOvercountedBatterySipper;
- @Mock
- private BatterySipper mUnaccountedBatterySipper;
- @Mock
- private BatterySipper mSystemBatterySipper;
- @Mock
- private BatterySipper mCellBatterySipper;
- @Mock
- private BatterySipper mIdleBatterySipper;
+ private SystemBatteryConsumer mSystemBatteryConsumer;
@Mock
private BatteryInfo mBatteryInfo;
@Mock
- private Bundle mBundle;
- @Mock
- private UserManager mUserManager;
- @Mock
private PackageManager mPackageManager;
@Mock
private AppOpsManager mAppOpsManager;
@Mock
private ApplicationInfo mApplicationInfo;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryStatsHelper;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryStatsManager mBatteryStatsManager;
@Mock
private ApplicationInfo mHighApplicationInfo;
@@ -172,7 +136,6 @@
private BatteryUtils mBatteryUtils;
private FakeFeatureFactory mFeatureFactory;
private PowerUsageFeatureProvider mProvider;
- private List<BatterySipper> mUsageList;
private Context mContext;
@Before
@@ -192,8 +155,6 @@
anyLong(), anyInt());
doReturn(TIME_STATE_BACKGROUND).when(mUid).getProcessStateTime(eq(PROCESS_STATE_BACKGROUND),
anyLong(), anyInt());
- when(mBatteryStatsHelper.getStats().computeBatteryRealtime(anyLong(), anyInt())).thenReturn(
- TIME_SINCE_LAST_FULL_CHARGE_US);
when(mPackageManager.getApplicationInfo(eq(HIGH_SDK_PACKAGE), anyInt()))
.thenReturn(mHighApplicationInfo);
@@ -202,32 +163,6 @@
mHighApplicationInfo.targetSdkVersion = Build.VERSION_CODES.O;
mLowApplicationInfo.targetSdkVersion = Build.VERSION_CODES.L;
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- mNormalBatterySipper.totalPowerMah = TOTAL_BATTERY_USAGE;
- doReturn(UID).when(mNormalBatterySipper).getUid();
-
- mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI;
- mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE;
-
- mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
- mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE;
-
- mScreenBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
- mScreenBatterySipper.totalPowerMah = BATTERY_SCREEN_USAGE;
-
- mSystemBatterySipper.drainType = BatterySipper.DrainType.APP;
- mSystemBatterySipper.totalPowerMah = BATTERY_SYSTEM_USAGE;
- when(mSystemBatterySipper.getUid()).thenReturn(Process.SYSTEM_UID);
-
- mOvercountedBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
- mOvercountedBatterySipper.totalPowerMah = BATTERY_OVERACCOUNTED_USAGE;
-
- mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
- mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE;
-
- mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE;
- mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE;
-
mContext = spy(RuntimeEnvironment.application);
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mAppOpsManager).when(mContext).getSystemService(Context.APP_OPS_SERVICE);
@@ -239,15 +174,6 @@
.getForegroundServiceTotalTimeUs(any(BatteryStats.Uid.class), anyLong());
mAnomalyInfo = new AnomalyInfo(INFO_WAKELOCK);
- mUsageList = new ArrayList<>();
- mUsageList.add(mNormalBatterySipper);
- mUsageList.add(mScreenBatterySipper);
- mUsageList.add(mCellBatterySipper);
- when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
- when(mBatteryStatsHelper.getTotalPower())
- .thenReturn(TOTAL_BATTERY_USAGE + BATTERY_SCREEN_USAGE);
- when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt()))
- .thenReturn(DISCHARGE_AMOUNT);
BatteryDatabaseManager.setUpForTest(mBatteryDatabaseManager);
ShadowThreadUtils.setIsMainThread(true);
}
@@ -291,173 +217,71 @@
}
@Test
- public void testRemoveHiddenBatterySippers_ContainsHiddenSippers_RemoveAndReturnValue() {
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(mNormalBatterySipper);
- sippers.add(mScreenBatterySipper);
- sippers.add(mSystemBatterySipper);
- sippers.add(mOvercountedBatterySipper);
- sippers.add(mUnaccountedBatterySipper);
- sippers.add(mWifiBatterySipper);
- sippers.add(mBluetoothBatterySipper);
- sippers.add(mIdleBatterySipper);
- when(mProvider.isTypeSystem(mSystemBatterySipper)).thenReturn(true);
- doNothing().when(mBatteryUtils).smearScreenBatterySipper(any(), any());
-
- final double totalUsage = mBatteryUtils.removeHiddenBatterySippers(sippers);
-
- assertThat(sippers).containsExactly(mNormalBatterySipper);
- assertThat(totalUsage).isWithin(PRECISION).of(BATTERY_SYSTEM_USAGE);
+ public void testShouldHideSystemConsumer_TypeIdle_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_IDLE);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeUnAccounted_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_TypeMobileRadio_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_MOBILE_RADIO);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeOverAccounted_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.OVERCOUNTED;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_TypeScreen_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_SCREEN);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeIdle_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.IDLE;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_TypeBluetooth_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_BLUETOOTH);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeCell_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.CELL;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_TypeWifi_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_WIFI);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeScreen_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.SCREEN;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_LowPower_ReturnTrue() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_FLASHLIGHT);
+ when(mSystemBatteryConsumer.getConsumedPower()).thenReturn(0.0005);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isTrue();
}
@Test
- public void testShouldHideSipper_TypeWifi_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.WIFI;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeBluetooth_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_TypeSystem_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(Process.ROOT_UID);
- when(mProvider.isTypeSystem(any())).thenReturn(true);
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_UidNormal_ReturnFalse() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(UID);
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isFalse();
- }
-
- @Test
- public void testShouldHideSipper_TypeService_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(UID);
- when(mProvider.isTypeService(any())).thenReturn(true);
-
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
- }
-
- @Test
- public void testShouldHideSipper_hiddenSystemModule_ReturnTrue() {
- mNormalBatterySipper.drainType = BatterySipper.DrainType.APP;
- when(mNormalBatterySipper.getUid()).thenReturn(UID);
- when(mBatteryUtils.isHiddenSystemModule(mNormalBatterySipper)).thenReturn(true);
-
- assertThat(mBatteryUtils.shouldHideSipper(mNormalBatterySipper)).isTrue();
+ public void testShouldHideSystemConsumer_HighPower_ReturnFalse() {
+ when(mSystemBatteryConsumer.getDrainType())
+ .thenReturn(SystemBatteryConsumer.DRAIN_TYPE_FLASHLIGHT);
+ when(mSystemBatteryConsumer.getConsumedPower()).thenReturn(0.5);
+ assertThat(mBatteryUtils.shouldHideSystemBatteryConsumer(mSystemBatteryConsumer)).isFalse();
}
@Test
public void testCalculateBatteryPercent() {
assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE,
- HIDDEN_USAGE, DISCHARGE_AMOUNT))
+ DISCHARGE_AMOUNT))
.isWithin(PRECISION).of(PERCENT_SYSTEM_USAGE);
}
@Test
- public void testSmearScreenBatterySipper() {
- final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ZERO,
- BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */);
- final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ZERO,
- BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */);
- final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND,
- BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */);
- final BatterySipper sipperFg2 = createTestSmearBatterySipper(TIME_FOREGROUND,
- BATTERY_APP_USAGE, 3 /* uid */, false /* isUidNull */);
-
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(sipperNull);
- sippers.add(sipperBg);
- sippers.add(sipperFg);
- sippers.add(sipperFg2);
-
- mBatteryUtils.smearScreenBatterySipper(sippers, mScreenBatterySipper);
-
- assertThat(sipperNull.totalPowerMah).isWithin(PRECISION).of(BATTERY_APP_USAGE);
- assertThat(sipperBg.totalPowerMah).isWithin(PRECISION).of(BATTERY_APP_USAGE);
- assertThat(sipperFg.totalPowerMah).isWithin(PRECISION).of(
- BATTERY_APP_USAGE + BATTERY_SCREEN_USAGE / 2);
- assertThat(sipperFg2.totalPowerMah).isWithin(PRECISION).of(
- BATTERY_APP_USAGE + BATTERY_SCREEN_USAGE / 2);
- }
-
- @Test
- public void testSmearScreenBatterySipper_screenSipperNull_shouldNotCrash() {
- final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND,
- BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */);
-
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(sipperFg);
-
- // Shouldn't crash
- mBatteryUtils.smearScreenBatterySipper(sippers, null /* screenSipper */);
- }
-
- @Test
- public void testCalculateRunningTimeBasedOnStatsType() {
- assertThat(mBatteryUtils.calculateRunningTimeBasedOnStatsType(mBatteryStatsHelper,
- BatteryStats.STATS_SINCE_CHARGED)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS);
- }
-
- @Test
- public void testSortUsageList() {
- final List<BatterySipper> sippers = new ArrayList<>();
- sippers.add(mNormalBatterySipper);
- sippers.add(mScreenBatterySipper);
- sippers.add(mSystemBatterySipper);
-
- mBatteryUtils.sortUsageList(sippers);
-
- assertThat(sippers).containsExactly(mNormalBatterySipper, mSystemBatterySipper,
- mScreenBatterySipper);
- }
-
- @Test
public void testCalculateLastFullChargeTime() {
final long currentTimeMs = System.currentTimeMillis();
- when(mBatteryStatsHelper.getStats().getStartClockTime()).thenReturn(
+ when(mBatteryUsageStats.getStatsStartRealtime()).thenReturn(
currentTimeMs - TIME_SINCE_LAST_FULL_CHARGE_MS);
- assertThat(mBatteryUtils.calculateLastFullChargeTime(
- mBatteryStatsHelper, currentTimeMs)).isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS);
+ assertThat(mBatteryUtils.calculateLastFullChargeTime(mBatteryUsageStats, currentTimeMs))
+ .isEqualTo(TIME_SINCE_LAST_FULL_CHARGE_MS);
}
@Test
@@ -509,56 +333,6 @@
.isFalse();
}
- private BatterySipper createTestSmearBatterySipper(
- long topTime, double totalPowerMah, int uidCode, boolean isUidNull) {
- final BatterySipper sipper = mock(BatterySipper.class);
- sipper.drainType = BatterySipper.DrainType.APP;
- sipper.totalPowerMah = totalPowerMah;
- doReturn(uidCode).when(sipper).getUid();
- if (!isUidNull) {
- final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
- doReturn(topTime).when(mBatteryUtils).getProcessTimeMs(
- eq(BatteryUtils.StatusType.SCREEN_USAGE), eq(uid), anyInt());
- doReturn(uidCode).when(uid).getUid();
- sipper.uidObj = uid;
- }
-
- return sipper;
- }
-
- @Test
- public void testInitBatteryStatsHelper_init() {
- mBatteryUtils.initBatteryStatsHelper(mBatteryStatsHelper, mBundle, mUserManager);
-
- verify(mBatteryStatsHelper).create(mBundle);
- verify(mBatteryStatsHelper).refreshStats(BatteryStats.STATS_SINCE_CHARGED,
- mUserManager.getUserProfiles());
- }
-
- @Test
- public void testFindBatterySipperByType_findTypeScreen() {
- BatterySipper sipper = mBatteryUtils.findBatterySipperByType(mUsageList,
- BatterySipper.DrainType.SCREEN);
-
- assertThat(sipper).isSameInstanceAs(mScreenBatterySipper);
- }
-
- @Test
- public void testFindBatterySipperByType_findTypeApp() {
- BatterySipper sipper = mBatteryUtils.findBatterySipperByType(mUsageList,
- BatterySipper.DrainType.APP);
-
- assertThat(sipper).isSameInstanceAs(mNormalBatterySipper);
- }
-
- @Test
- public void testCalculateScreenUsageTime_returnCorrectTime() {
- mScreenBatterySipper.usageTimeMs = TIME_EXPECTED_FOREGROUND;
-
- assertThat(mBatteryUtils.calculateScreenUsageTime(mBatteryStatsHelper)).isEqualTo(
- TIME_EXPECTED_FOREGROUND);
- }
-
@Test
public void testIsPreOApp_SdkLowerThanO_ReturnTrue() {
assertThat(mBatteryUtils.isPreOApp(LOW_SDK_PACKAGE)).isTrue();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
index 1ef2880..c9b1a00 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageAdvancedTest.java
@@ -69,6 +69,14 @@
mFeatureFactory = FakeFeatureFactory.setupForTest();
when(mToggleAppsMenu.getItemId()).thenReturn(PowerUsageAdvanced.MENU_TOGGLE_APPS);
+ BatteryAppListPreferenceController.sConfig =
+ new BatteryAppListPreferenceController.Config() {
+ @Override
+ public boolean shouldShowBatteryAttributionList(Context context) {
+ return true;
+ }
+ };
+
mFragment = spy(new PowerUsageAdvanced());
mFragment.onAttach(mContext);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java
index 8754700..451e605 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageBaseTest.java
@@ -27,7 +27,6 @@
import androidx.loader.app.LoaderManager;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.testutils.shadow.ShadowDashboardFragment;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -46,8 +45,6 @@
public class PowerUsageBaseTest {
@Mock
- private BatteryStatsHelper mBatteryStatsHelper;
- @Mock
private LoaderManager mLoaderManager;
private TestFragment mFragment;
@@ -56,7 +53,6 @@
MockitoAnnotations.initMocks(this);
mFragment = spy(new TestFragment());
- mFragment.setBatteryStatsHelper(mBatteryStatsHelper);
doReturn(mLoaderManager).when(mFragment).getLoaderManager();
}
@@ -98,9 +94,5 @@
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return null;
}
-
- private void setBatteryStatsHelper(BatteryStatsHelper batteryStatsHelper) {
- mStatsHelper = batteryStatsHelper;
- }
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
index e345ab2..11b6ad2 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java
@@ -21,7 +21,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -40,8 +39,6 @@
import androidx.loader.app.LoaderManager;
import androidx.preference.PreferenceScreen;
-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.fuelgauge.batterytip.BatteryTipPreferenceController;
@@ -54,7 +51,6 @@
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -62,35 +58,19 @@
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
-import java.util.ArrayList;
import java.util.List;
// TODO: Improve this test class so that it starts up the real activity and fragment.
@RunWith(RobolectricTestRunner.class)
public class PowerUsageSummaryTest {
- private static final int UID = 123;
- 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;
private static Intent sAdditionalBatteryInfoIntent;
@BeforeClass
public static void beforeClass() {
sAdditionalBatteryInfoIntent = new Intent("com.example.app.ADDITIONAL_BATTERY_INFO");
}
-
- @Mock
- private BatterySipper mNormalBatterySipper;
- @Mock
- private BatterySipper mScreenBatterySipper;
- @Mock
- private BatterySipper mCellBatterySipper;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryHelper;
@Mock
private SettingsActivity mSettingsActivity;
@Mock
@@ -104,7 +84,6 @@
@Mock
private PreferenceScreen mPreferenceScreen;
- private List<BatterySipper> mUsageList;
private Context mRealContext;
private TestFragment mFragment;
private FakeFeatureFactory mFeatureFactory;
@@ -123,27 +102,6 @@
when(mFragment.getActivity()).thenReturn(mSettingsActivity);
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;
-
- 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.mBatteryUtils = spy(new BatteryUtils(mRealContext));
ReflectionHelpers.setField(mFragment, "mVisibilityLoggerMixin", mVisibilityLoggerMixin);
ReflectionHelpers.setField(mFragment, "mBatteryBroadcastReceiver",
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index c97d79f..82448d1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -24,9 +24,9 @@
import android.content.Context;
import android.content.Intent;
+import android.os.BatteryUsageStats;
import android.os.PowerManager;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.tips.AppLabelPredicate;
@@ -57,7 +57,7 @@
BatteryTip.TipType.SUMMARY,
BatteryTip.TipType.SMART_BATTERY_MANAGER};
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryStatsHelper;
+ private BatteryUsageStats mBatteryUsageStats;
@Mock
private PowerManager mPowerManager;
@Mock
@@ -78,7 +78,7 @@
doReturn(mPowerManager).when(mContext).getSystemService(Context.POWER_SERVICE);
doReturn(mIntent).when(mContext).registerReceiver(any(), any());
doReturn(mBatteryInfo).when(mBatteryUtils).getBatteryInfo(any());
- mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryStatsHelper);
+ mBatteryTipLoader = new BatteryTipLoader(mContext, mBatteryUsageStats);
mBatteryTipLoader.mBatteryUtils = mBatteryUtils;
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
index 93005d5..c125876 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/HighUsageDetectorTest.java
@@ -19,23 +19,20 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.Intent;
-import android.os.BatteryStats;
+import android.os.BatteryManager;
import android.os.BatteryStatsManager;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
+import android.os.UidBatteryConsumer;
-import com.android.internal.os.BatterySipper;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
@@ -46,7 +43,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -64,15 +60,14 @@
private static final int UID_LOW = 345;
private static final double POWER_HIGH = 20000;
private static final double POWER_LOW = 10000;
+
private Context mContext;
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private BatteryStatsHelper mBatteryStatsHelper;
@Mock
- private BatterySipper mHighBatterySipper;
+ private UidBatteryConsumer mHighBatteryConsumer;
@Mock
- private BatterySipper mLowBatterySipper;
+ private UidBatteryConsumer mLowBatteryConsumer;
@Mock
- private BatterySipper mSystemBatterySipper;
+ private UidBatteryConsumer mSystemBatteryConsumer;
@Mock
private HighUsageDataParser mDataParser;
@Mock
@@ -85,7 +80,6 @@
private BatteryTipPolicy mPolicy;
private BatteryUtils mBatteryUtils;
private HighUsageDetector mHighUsageDetector;
- private List<BatterySipper> mUsageList;
@Before
public void setUp() {
@@ -100,27 +94,22 @@
when(mBatteryStatsManager.getBatteryUsageStats(any(BatteryUsageStatsQuery.class)))
.thenReturn(mBatteryUsageStats);
- mContext.sendStickyBroadcast(new Intent(Intent.ACTION_BATTERY_CHANGED));
+ mContext.sendStickyBroadcast(new Intent(Intent.ACTION_BATTERY_CHANGED)
+ .putExtra(BatteryManager.EXTRA_PLUGGED, 0));
- mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryStatsHelper,
+ mHighUsageDetector = spy(new HighUsageDetector(mContext, mPolicy, mBatteryUsageStats,
mBatteryUtils.getBatteryInfo(TAG)));
mHighUsageDetector.mBatteryUtils = mBatteryUtils;
mHighUsageDetector.mDataParser = mDataParser;
doNothing().when(mHighUsageDetector).parseBatteryData();
- doReturn(UID_HIGH).when(mHighBatterySipper).getUid();
- doReturn(UID_LOW).when(mLowBatterySipper).getUid();
- mHighBatterySipper.uidObj = mock(BatteryStats.Uid.class);
- mHighBatterySipper.drainType = BatterySipper.DrainType.APP;
- mHighBatterySipper.totalSmearedPowerMah = POWER_HIGH;
- mLowBatterySipper.uidObj = mock(BatteryStats.Uid.class);
- mLowBatterySipper.drainType = BatterySipper.DrainType.APP;
- mLowBatterySipper.totalSmearedPowerMah = POWER_LOW;
- when(mBatteryUtils.shouldHideSipper(mSystemBatterySipper)).thenReturn(true);
- when(mBatteryUtils.shouldHideSipper(mHighBatterySipper)).thenReturn(false);
- when(mBatteryUtils.shouldHideSipper(mLowBatterySipper)).thenReturn(false);
- when(mBatteryStatsHelper.getStats().getDischargeAmount(anyInt())).thenReturn(100);
- when(mBatteryStatsHelper.getTotalPower()).thenReturn(POWER_HIGH + POWER_LOW);
-
+ doReturn(UID_HIGH).when(mHighBatteryConsumer).getUid();
+ doReturn(UID_LOW).when(mLowBatteryConsumer).getUid();
+ doReturn(POWER_HIGH).when(mHighBatteryConsumer).getConsumedPower();
+ doReturn(POWER_LOW).when(mLowBatteryConsumer).getConsumedPower();
+ doReturn(false).when(mBatteryUtils).shouldHideUidBatteryConsumer(mHighBatteryConsumer);
+ doReturn(false).when(mBatteryUtils).shouldHideUidBatteryConsumer(mLowBatteryConsumer);
+ when(mBatteryUsageStats.getDischargePercentage()).thenReturn(100);
+ when(mBatteryUsageStats.getConsumedPower()).thenReturn(POWER_HIGH + POWER_LOW);
mHighAppInfo = new AppInfo.Builder()
.setUid(UID_HIGH)
@@ -129,11 +118,11 @@
.setUid(UID_LOW)
.build();
- mUsageList = new ArrayList<>();
- mUsageList.add(mSystemBatterySipper);
- mUsageList.add(mLowBatterySipper);
- mUsageList.add(mHighBatterySipper);
- when(mBatteryStatsHelper.getUsageList()).thenReturn(mUsageList);
+ ArrayList<UidBatteryConsumer> consumers = new ArrayList<>();
+ consumers.add(mSystemBatteryConsumer);
+ consumers.add(mLowBatteryConsumer);
+ consumers.add(mHighBatteryConsumer);
+ when(mBatteryUsageStats.getUidBatteryConsumers()).thenReturn(consumers);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index 40315d2..173f625 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -68,7 +68,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public void onStart_isNotDebuggable_shouldHideSystemOverlay() {
@@ -88,7 +87,6 @@
@Test
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class,
})
public void onStop_isNotDebuggable_shouldRemoveHideSystemOverlay() {
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
index 68c97cf..cffc4d1 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
@@ -23,15 +23,15 @@
import android.content.Context;
import android.net.Uri;
+import android.os.BatteryUsageStats;
import androidx.slice.Slice;
import androidx.slice.SliceMetadata;
import androidx.slice.SliceProvider;
import androidx.slice.widget.SliceLiveData;
-import com.android.internal.os.BatteryStatsHelper;
import com.android.settings.R;
-import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
+import com.android.settings.fuelgauge.BatteryUsageStatsLoader;
import com.android.settings.fuelgauge.batterytip.AppInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipLoader;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
@@ -57,7 +57,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
- BatteryFixSliceTest.ShadowBatteryStatsHelperLoader.class,
+ BatteryFixSliceTest.ShadowBatteryUsageStatsLoader.class,
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
public class BatteryFixSliceTest {
@@ -135,11 +135,11 @@
assertThat(ShadowEarlyWarningTip.isIconTintColorIdCalled()).isTrue();
}
- @Implements(BatteryStatsHelperLoader.class)
- public static class ShadowBatteryStatsHelperLoader {
+ @Implements(BatteryUsageStatsLoader.class)
+ public static class ShadowBatteryUsageStatsLoader {
@Implementation
- protected BatteryStatsHelper loadInBackground() {
+ protected BatteryUsageStats loadInBackground() {
return null;
}
}
diff --git a/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java
index d7d76be..f5868c2 100644
--- a/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/location/LocationServicesPreferenceControllerTest.java
@@ -19,13 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.content.Context;
-import android.net.wifi.WifiManager;
-import android.provider.Settings;
-
-import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
@@ -39,7 +34,6 @@
@RunWith(RobolectricTestRunner.class)
public class LocationServicesPreferenceControllerTest {
@Mock
- private WifiManager mWifiManager;
private Context mContext;
private LocationServicesPreferenceController mController;
@@ -47,7 +41,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
- when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mController = new LocationServicesPreferenceController(mContext, "key");
}
@@ -57,42 +50,6 @@
}
@Test
- public void testLocationScanning_WifiOnBleOn() {
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
- assertThat(mController.getSummary()).isEqualTo(
- mContext.getString(R.string.scanning_status_text_wifi_on_ble_on));
- }
-
- @Test
- public void testLocationScanning_WifiOnBleOff() {
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
- assertThat(mController.getSummary()).isEqualTo(
- mContext.getString(R.string.scanning_status_text_wifi_on_ble_off));
- }
-
- @Test
- public void testLocationScanning_WifiOffBleOn() {
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
- assertThat(mController.getSummary()).isEqualTo(
- mContext.getString(R.string.scanning_status_text_wifi_off_ble_on));
- }
-
- @Test
- public void testLocationScanning_WifiOffBleOff() {
- when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
- assertThat(mController.getSummary()).isEqualTo(
- mContext.getString(R.string.scanning_status_text_wifi_off_ble_off));
- }
-
- @Test
@Config(qualifiers = "mcc999")
public void testLocationScanning_ifDisabled_shouldNotBeShown() {
assertThat(mController.isAvailable()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
index b269b05..8a81908 100644
--- a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
@@ -16,7 +16,6 @@
package com.android.settings.network;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
index 95d60a4..355dda8 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkListControllerTest.java
@@ -39,6 +39,10 @@
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
+import androidx.lifecycle.Lifecycle;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
@@ -52,10 +56,6 @@
import java.util.Arrays;
-import androidx.lifecycle.Lifecycle;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkListControllerTest {
@Mock
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
index a65ff24..fd2b520 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkListFragmentTest.java
@@ -33,7 +33,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
-
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkListFragmentTest {
@Mock
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
index 097ebaf..1639bba 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkSummaryControllerTest.java
@@ -32,7 +32,6 @@
import android.content.Context;
import android.content.Intent;
-import android.net.ConnectivityManager;
import android.os.UserManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
@@ -110,9 +109,7 @@
@Test
public void isAvailable_wifiOnlyMode_notAvailable() {
- final ConnectivityManager cm = mock(ConnectivityManager.class);
- when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
- when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
when(mUserManager.isAdminUser()).thenReturn(true);
assertThat(mController.isAvailable()).isFalse();
@@ -120,11 +117,8 @@
@Test
public void isAvailable_secondaryUser_notAvailable() {
- final ConnectivityManager cm = mock(ConnectivityManager.class);
- when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
- when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
+ when(mTelephonyManager.isDataCapable()).thenReturn(true);
when(mUserManager.isAdminUser()).thenReturn(false);
-
assertThat(mController.isAvailable()).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
index efb77eb..390a674 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
@@ -60,8 +60,8 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
index ea90860..44611ce 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkSettingsTest.java
@@ -74,7 +74,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
- when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mActivity.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
when(mContext.getSystemService(NetworkStatsManager.class)).thenReturn(mNetworkStatsManager);
ShadowEntityHeaderController.setUseMock(mock(EntityHeaderController.class));
diff --git a/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java b/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java
index 406f360..6abf8a0 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/SignalStrengthListenerTest.java
@@ -16,13 +16,9 @@
package com.android.settings.network.telephony;
-import static android.telephony.PhoneStateListener.LISTEN_NONE;
-import static android.telephony.PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -31,7 +27,7 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import org.junit.Before;
@@ -44,6 +40,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import java.util.concurrent.Executor;
+
@RunWith(RobolectricTestRunner.class)
public class SignalStrengthListenerTest {
private static final int SUB_ID_1 = 111;
@@ -88,13 +86,19 @@
@Test
public void updateSubscriptionIds_beforeResume_startedListening() {
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2));
- ArgumentCaptor<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
- ArgumentCaptor<PhoneStateListener> captor2 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager2).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager3, never()).listen(any(), anyInt());
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor1 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor2 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
+
+ verify(mManager1).registerTelephonyCallback(
+ any(Executor.class), captor1.capture());
+ verify(mManager2).registerTelephonyCallback(
+ any(Executor.class), captor2.capture());
+ verify(mManager3, never()).registerTelephonyCallback(any(), any());
+
assertThat(captor1.getValue()).isNotNull();
assertThat(captor2.getValue()).isNotNull();
@@ -105,46 +109,57 @@
@Test
public void updateSubscriptionIds_twoCalls_oneIdAdded() {
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2));
- verify(mManager1).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager2).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS));
+
+ verify(mManager1).registerTelephonyCallback(any(Executor.class),
+ eq(mListener.mTelephonyCallbacks.get(SUB_ID_1)));
+ verify(mManager2).registerTelephonyCallback(any(Executor.class),
+ eq(mListener.mTelephonyCallbacks.get(SUB_ID_2)));
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2, SUB_ID_3));
- verify(mManager1, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE));
- verify(mManager2, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE));
- verify(mManager3).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS));
+ verify(mManager1, never()).unregisterTelephonyCallback(
+ mListener.mTelephonyCallbacks.get(SUB_ID_1));
+ verify(mManager2, never()).unregisterTelephonyCallback(
+ mListener.mTelephonyCallbacks.get(SUB_ID_2));
+ verify(mManager3).registerTelephonyCallback(
+ any(Executor.class), eq(mListener.mTelephonyCallbacks.get(SUB_ID_3)));
}
@Test
public void updateSubscriptionIds_twoCalls_oneIdRemoved() {
- ArgumentCaptor<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor1 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2));
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager2).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS));
+ verify(mManager1).registerTelephonyCallback(any(Executor.class), captor1.capture());
+ verify(mManager2).registerTelephonyCallback(
+ any(Executor.class), any(TelephonyCallback.class));
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_2));
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE));
- verify(mManager2, never()).listen(any(PhoneStateListener.class), eq(LISTEN_NONE));
+ verify(mManager1).unregisterTelephonyCallback(captor1.capture());
+ verify(mManager2, never()).unregisterTelephonyCallback(any(TelephonyCallback.class));
// Make sure the correct listener was removed.
assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue();
}
@Test
public void updateSubscriptionIds_twoCalls_twoIdsRemovedOneAdded() {
- ArgumentCaptor<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
- ArgumentCaptor<PhoneStateListener> captor2 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor1 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor2 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_1, SUB_ID_2));
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager2).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
+ verify(mManager1).registerTelephonyCallback(any(Executor.class), captor1.capture());
+ verify(mManager2).registerTelephonyCallback(any(Executor.class), captor2.capture());
mListener.updateSubscriptionIds(Sets.newSet(SUB_ID_3));
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE));
- verify(mManager2).listen(captor2.capture(), eq(LISTEN_NONE));
- verify(mManager3).listen(any(PhoneStateListener.class), eq(LISTEN_SIGNAL_STRENGTHS));
+ verify(mManager1).unregisterTelephonyCallback(captor1.capture());
+ verify(mManager2).unregisterTelephonyCallback(captor2.capture());
+ verify(mManager3).registerTelephonyCallback(
+ any(Executor.class), any(TelephonyCallback.class));
// Make sure the correct listeners were removed.
assertThat(captor1.getValue() != captor2.getValue()).isTrue();
assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue();
@@ -157,15 +172,19 @@
mListener.pause();
mListener.resume();
- ArgumentCaptor<PhoneStateListener> captor1 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
- ArgumentCaptor<PhoneStateListener> captor2 = ArgumentCaptor.forClass(
- PhoneStateListener.class);
- verify(mManager1, times(2)).listen(captor1.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager1).listen(captor1.capture(), eq(LISTEN_NONE));
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor1 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
+ ArgumentCaptor<SignalStrengthListener.SignalStrengthTelephonyCallback> captor2 =
+ ArgumentCaptor.forClass(
+ SignalStrengthListener.SignalStrengthTelephonyCallback.class);
+ verify(mManager1, times(2)).registerTelephonyCallback(
+ any(Executor.class), captor1.capture());
+ verify(mManager1).unregisterTelephonyCallback(captor1.capture());
- verify(mManager2, times(2)).listen(captor2.capture(), eq(LISTEN_SIGNAL_STRENGTHS));
- verify(mManager2).listen(captor2.capture(), eq(LISTEN_NONE));
+ verify(mManager2, times(2)).registerTelephonyCallback(
+ any(Executor.class), captor2.capture());
+ verify(mManager2).unregisterTelephonyCallback(captor2.capture());
assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(1)).isTrue();
assertThat(captor1.getAllValues().get(0) == captor1.getAllValues().get(2)).isTrue();
diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
index 634b9a8..6b92e57 100644
--- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
+++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java
@@ -65,7 +65,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
@@ -233,7 +232,6 @@
}
@Test
- @Ignore
public void updatePreferencesOrFinish_callingAppIsAdmin_deviceNotProvisioned_footerInvisible() {
Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 0);
initActivity(new Intent().putExtra(EXTRA_KEY_IS_CALLING_APP_ADMIN, true));
diff --git a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
index 79d4b6b..af44d39 100644
--- a/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
+++ b/tests/robotests/src/com/android/settings/password/SetNewPasswordActivityTest.java
@@ -50,7 +50,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -105,18 +104,17 @@
}
@Test
- @Ignore
public void testSetupChooseLockGeneric() {
Settings.Global.putInt(RuntimeEnvironment.application.getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0);
+ Intent intent = new Intent(ACTION_SET_NEW_PASSWORD);
+ intent.putExtra(WizardManagerHelper.EXTRA_IS_SETUP_FLOW, true);
SetNewPasswordActivity activity =
- Robolectric.buildActivity(SetNewPasswordActivity.class).get();
- activity.launchChooseLock(new Bundle());
+ Robolectric.buildActivity(SetNewPasswordActivity.class, intent).create().get();
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
- Intent intent = getLaunchChooseLockIntent(shadowActivity);
- intent.putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true);
- assertThat(intent.getComponent())
+ Intent nextIntent = shadowActivity.getNextStartedActivityForResult().intent;
+ assertThat(nextIntent.getComponent())
.isEqualTo(new ComponentName(activity, SetupChooseLockGeneric.class));
}
diff --git a/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java
new file mode 100644
index 0000000..c707cd6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class ShowClipAccessNotificationPreferenceControllerTest {
+
+ @Mock
+ private PreferenceScreen mScreen;
+
+ private Context mContext;
+ private ShowClipAccessNotificationPreferenceController mController;
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = ApplicationProvider.getApplicationContext();
+ mController = new ShowClipAccessNotificationPreferenceController(mContext);
+ mPreference = new Preference(mContext);
+ mPreference.setKey(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void isChecked_settingIsOff_shouldReturnFalse() throws Exception {
+ setProperty(0);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_settingIsOn_shouldReturnTrue() throws Exception {
+ setProperty(1);
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void onPreferenceChange_turnOn_shouldChangeSettingTo1() throws Exception {
+ setProperty(0);
+
+ mController.onPreferenceChange(mPreference, true);
+
+ assertThat(mController.isChecked()).isTrue();
+ assertProperty(1);
+ }
+
+ @Test
+ public void onPreferenceChange_turnOff_shouldChangeSettingTo0() throws Exception {
+ setProperty(1);
+
+ mController.onPreferenceChange(mPreference, false);
+
+ assertThat(mController.isChecked()).isFalse();
+ assertProperty(0);
+ }
+
+ private void setProperty(int newValue) {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ Settings.Secure.putInt(
+ contentResolver, Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, newValue);
+ }
+
+ private void assertProperty(int expectedValue) throws SettingNotFoundException {
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ assertThat(Settings.Secure.getInt(
+ contentResolver, Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS))
+ .isEqualTo(expectedValue);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
index a6d3354..10e291c 100644
--- a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
+++ b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
@@ -15,7 +15,6 @@
*/
package com.android.settings.sim;
-
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS;
import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
diff --git a/tests/robotests/src/com/android/settings/wifi/CellularFallbackPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/CellularFallbackPreferenceControllerTest.java
index 6f3230c..81f8e63 100644
--- a/tests/robotests/src/com/android/settings/wifi/CellularFallbackPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/CellularFallbackPreferenceControllerTest.java
@@ -24,14 +24,12 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.content.res.Resources;
import android.telephony.SubscriptionManager;
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;
diff --git a/tests/robotests/src/com/android/settings/wifi/NotifyOpenNetworksPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/NotifyOpenNetworksPreferenceControllerTest.java
index f9271a6..13842b5 100644
--- a/tests/robotests/src/com/android/settings/wifi/NotifyOpenNetworksPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/NotifyOpenNetworksPreferenceControllerTest.java
@@ -26,7 +26,6 @@
import android.content.Context;
import android.provider.Settings;
-import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import org.junit.Before;
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java b/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java
index 4cfc9ba..8478a54 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/DisclaimerItemListAdapterTest.java
@@ -17,9 +17,10 @@
package com.android.settings.wifi.calling;
import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
- .DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_TITLE;
-import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
.DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_DESCRIPTION;
+import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
+ .DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_TITLE;
+
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
@@ -33,11 +34,6 @@
import android.view.ViewGroup;
import android.widget.TextView;
-import com.android.settings.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,6 +41,9 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class DisclaimerItemListAdapterTest {
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/EmergencyCallLimitationDisclaimerTest.java b/tests/robotests/src/com/android/settings/wifi/calling/EmergencyCallLimitationDisclaimerTest.java
index ce80324..985edda 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/EmergencyCallLimitationDisclaimerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/EmergencyCallLimitationDisclaimerTest.java
@@ -17,10 +17,10 @@
package com.android.settings.wifi.calling;
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -29,11 +29,9 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import com.android.settings.R;
-
import org.junit.Before;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java b/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java
index 9494288..3fe9678 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/LocationPolicyDisclaimerTest.java
@@ -17,12 +17,10 @@
package com.android.settings.wifi.calling;
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.anyBoolean;
+
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -31,11 +29,9 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import com.android.settings.R;
-
import org.junit.Before;
-import org.junit.runner.RunWith;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index 8ab3ad2..a3c2535 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -16,12 +16,12 @@
package com.android.settings.wifi.calling;
+import static junit.framework.Assert.assertEquals;
+
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertEquals;
-
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java
index 25ad730..a423071 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java
@@ -16,7 +16,7 @@
package com.android.settings.wifi.tether;
-import static org.mockito.ArgumentMatchers.anyString;;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
index a241296..2ecc7d2 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -101,7 +101,7 @@
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
- assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND);
+ assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
@@ -115,7 +115,7 @@
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
- assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_AP_BAND);
+ assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index b55a788..b7ac4b1 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -32,6 +32,8 @@
"platform-test-annotations",
"truth-prebuilt",
"ub-uiautomator",
+ "SettingsLibSettingsSpinner",
+ "SettingsLibUsageProgressBarPreference",
],
// Include all test java files.
diff --git a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java
index 9c50d61..f186238 100644
--- a/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java
+++ b/tests/unit/src/com/android/settings/applications/manageapplications/AppFilterRegistryTest.java
@@ -19,6 +19,7 @@
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_ALARMS_AND_REMINDERS;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_INSTALL_SOURCES;
+import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_MEDIA_MANAGEMENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_ALLOWLIST;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_USAGE_ACCESS;
@@ -29,6 +30,7 @@
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_HIGH_POWER;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MAIN;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MANAGE_SOURCES;
+import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MEDIA_MANAGEMENT_APPS;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MOVIES;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION;
import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_OVERLAY;
@@ -64,6 +66,9 @@
assertThat(registry.getDefaultFilterType(LIST_TYPE_ALARMS_AND_REMINDERS))
.isEqualTo(FILTER_ALARMS_AND_REMINDERS);
+ assertThat(registry.getDefaultFilterType(LIST_TYPE_MEDIA_MANAGEMENT_APPS))
+ .isEqualTo(FILTER_APPS_MEDIA_MANAGEMENT);
+
assertThat(registry.getDefaultFilterType(LIST_TYPE_MAIN)).isEqualTo(FILTER_APPS_ALL);
assertThat(registry.getDefaultFilterType(LIST_TYPE_NOTIFICATION))
.isEqualTo(FILTER_APPS_RECENT);
diff --git a/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java
new file mode 100644
index 0000000..314f8c2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.view.Menu;
+
+import androidx.fragment.app.Fragment;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.deviceinfo.storage.StorageEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class VolumeOptionMenuControllerTest {
+
+ private static final String INTERNAL_VOLUME_ID = "1";
+ private static final String EXTERNAL_VOLUME_ID = "2";
+ private static final String DISK_ID = "3";
+ private static final String VOLUME_RECORD_FSUUID = "volume_record_fsuuid";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Menu mMenu;
+ @Mock private PackageManager mPackageManager;
+ @Mock private StorageManager mStorageManager;
+ @Mock private VolumeInfo mExternalVolumeInfo;
+ @Mock private VolumeInfo mInternalVolumeInfo;
+
+ private Context mContext;
+ private VolumeOptionMenuController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+
+ when(mInternalVolumeInfo.getId()).thenReturn(INTERNAL_VOLUME_ID);
+ when(mInternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+ when(mInternalVolumeInfo.isMountedWritable()).thenReturn(true);
+ when(mExternalVolumeInfo.getId()).thenReturn(EXTERNAL_VOLUME_ID);
+
+ final StorageEntry selectedStorageEntry = new StorageEntry(mContext, mInternalVolumeInfo);
+ mController = new VolumeOptionMenuController(mContext, mock(Fragment.class),
+ selectedStorageEntry);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_unSupportedDiskInfo_formatIsVisible() {
+ final StorageEntry unsupportedStorageEntry =
+ new StorageEntry(new DiskInfo(DISK_ID, 0 /* flags */));
+ mController.setSelectedStorageEntry(unsupportedStorageEntry);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mFormat, atLeastOnce()).setVisible(true);
+ verify(mController.mRename, never()).setVisible(true);
+ verify(mController.mMount, never()).setVisible(true);
+ verify(mController.mUnmount, never()).setVisible(true);
+ verify(mController.mFormatAsPortable, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mMigrate, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ verify(mController.mForget, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_missingVolumeRecord_forgetIsVisible() {
+ final StorageEntry missingStorageEntry =
+ new StorageEntry(new VolumeRecord(0 /* type */, VOLUME_RECORD_FSUUID));
+ mController.setSelectedStorageEntry(missingStorageEntry);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mForget, atLeastOnce()).setVisible(true);
+ verify(mController.mRename, never()).setVisible(true);
+ verify(mController.mMount, never()).setVisible(true);
+ verify(mController.mUnmount, never()).setVisible(true);
+ verify(mController.mFormat, never()).setVisible(true);
+ verify(mController.mFormatAsPortable, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mMigrate, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_unmountedStorage_mountIsVisible() {
+ when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTED);
+ mController.setSelectedStorageEntry(new StorageEntry(mContext, mInternalVolumeInfo));
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mMount, atLeastOnce()).setVisible(true);
+ verify(mController.mRename, never()).setVisible(true);
+ verify(mController.mUnmount, never()).setVisible(true);
+ verify(mController.mFormat, never()).setVisible(true);
+ verify(mController.mFormatAsPortable, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mMigrate, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ verify(mController.mForget, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_privateNotDefaultInternal_someMenusAreVisible() {
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mRename, atLeastOnce()).setVisible(true);
+ verify(mController.mUnmount, atLeastOnce()).setVisible(true);
+ verify(mController.mFormatAsPortable, atLeastOnce()).setVisible(true);
+ verify(mController.mMount, never()).setVisible(true);
+ verify(mController.mFormat, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ verify(mController.mForget, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_privateDefaultInternal_mostMenusAreNotVisible() {
+ when(mInternalVolumeInfo.getId()).thenReturn(VolumeInfo.ID_PRIVATE_INTERNAL);
+ when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mRename, never()).setVisible(true);
+ verify(mController.mUnmount, never()).setVisible(true);
+ verify(mController.mFormatAsPortable, never()).setVisible(true);
+ verify(mController.mMount, never()).setVisible(true);
+ verify(mController.mFormat, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ verify(mController.mForget, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_publicStorage_someMenusArcVisible() {
+ when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
+ when(mExternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+ when(mExternalVolumeInfo.getDiskId()).thenReturn(DISK_ID);
+ final DiskInfo externalDiskInfo = mock(DiskInfo.class);
+ when(mStorageManager.findDiskById(DISK_ID)).thenReturn(externalDiskInfo);
+ mController.setSelectedStorageEntry(new StorageEntry(mContext, mExternalVolumeInfo));
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mRename, atLeastOnce()).setVisible(true);
+ verify(mController.mUnmount, atLeastOnce()).setVisible(true);
+ verify(mController.mFormat, atLeastOnce()).setVisible(true);
+ verify(mController.mMount, never()).setVisible(true);
+ verify(mController.mFormatAsPortable, never()).setVisible(true);
+ verify(mController.mFormatAsInternal, never()).setVisible(true);
+ verify(mController.mFree, never()).setVisible(true);
+ verify(mController.mForget, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_noExternalStorage_migrateNotVisible() {
+ when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mMigrate, atLeastOnce()).setVisible(false);
+ verify(mController.mMigrate, never()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_externalPrimaryStorageAvailable_migrateIsVisible() {
+ when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mExternalVolumeInfo.isMountedWritable()).thenReturn(true);
+ when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mMigrate, atLeastOnce()).setVisible(true);
+ }
+
+ @Test
+ public void onPrepareOptionsMenu_externalUnmounted_migrateIsVisible() {
+ when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mExternalVolumeInfo.isMountedWritable()).thenReturn(false);
+ when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
+
+ mController.onPrepareOptionsMenu(mMenu);
+
+ verify(mController.mMigrate, atLeastOnce()).setVisible(false);
+ verify(mController.mMigrate, never()).setVisible(true);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
index ae534d0..dfe2bc0 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogControllerTest.java
@@ -48,7 +48,6 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.CellSignalStrength;
-import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
@@ -89,8 +88,6 @@
@Mock
private ServiceState mServiceState;
@Mock
- private PhoneStateListener mPhoneStateListener;
- @Mock
private SignalStrength mSignalStrength;
@Mock
private CellSignalStrength mCellSignalStrengthCdma;
@@ -150,7 +147,6 @@
doReturn(0).when(mCellSignalStrengthWcdma).getAsuLevel();
doReturn(null).when(mSignalStrength).getCellSignalStrengths();
- doReturn(mPhoneStateListener).when(mController).getPhoneStateListener();
doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
when(mTelephonyManager.getActiveModemCount()).thenReturn(MAX_PHONE_COUNT_SINGLE_SIM);
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
new file mode 100644
index 0000000..cf1b6b2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageEntryTest {
+
+ private static final String VOLUME_INFO_ID = "volume_info_id";
+ private static final String DISK_INFO_ID = "disk_info_id";
+ private static final String VOLUME_RECORD_UUID = "volume_record_id";
+
+ @Mock
+ private VolumeInfo mVolumeInfo;
+ @Mock
+ private DiskInfo mDiskInfo;
+ @Mock
+ private VolumeRecord mVolumeRecord;
+
+ private Context mContext;
+ @Mock
+ private StorageManager mStorageManager;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+ }
+
+ @Test
+ public void equals_volumesOfSameId_shouldBeTheSame() {
+ final StorageEntry volumeStorage1 = new StorageEntry(mContext,
+ new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+ final StorageEntry volumeStorage2 = new StorageEntry(mContext,
+ new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+ final StorageEntry diskStorage1 =
+ new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+ final StorageEntry diskStorage2 =
+ new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+ final StorageEntry volumeRecordStorage1 = new StorageEntry(new VolumeRecord(0 /* flags */,
+ VOLUME_RECORD_UUID));
+ final StorageEntry volumeRecordStorage2 = new StorageEntry(new VolumeRecord(0 /* flags */,
+ VOLUME_RECORD_UUID));
+
+ assertThat(Objects.equals(volumeStorage1, volumeStorage2)).isTrue();
+ assertThat(Objects.equals(diskStorage1, diskStorage2)).isTrue();
+ assertThat(Objects.equals(volumeRecordStorage1, volumeRecordStorage2)).isTrue();
+ }
+
+ @Test
+ public void equals_volumesOfDifferentId_shouldBeDifferent() {
+ final StorageEntry volumeStorage1 = new StorageEntry(mContext,
+ new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+ final StorageEntry volumeStorage2 = new StorageEntry(mContext,
+ new VolumeInfo("id2", 0 /* type */, null /* disk */, null /* partGuid */));
+ final StorageEntry diskStorage1 =
+ new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+ final StorageEntry diskStorage2 =
+ new StorageEntry(new DiskInfo("id2", 0 /* flags */));
+ final StorageEntry volumeRecordStorage1 = new StorageEntry(new VolumeRecord(0 /* flags */,
+ VOLUME_RECORD_UUID));
+ final StorageEntry volumeRecordStorage2 = new StorageEntry(new VolumeRecord(0 /* flags */,
+ "id2"));
+
+ assertThat(Objects.equals(volumeStorage1, volumeStorage2)).isFalse();
+ assertThat(Objects.equals(diskStorage1, diskStorage2)).isFalse();
+ assertThat(Objects.equals(volumeRecordStorage1, volumeRecordStorage2)).isFalse();
+ }
+
+ @Test
+ public void compareTo_defaultInternalStorage_shouldBeAtTopMost() {
+ final StorageEntry storage1 = mock(StorageEntry.class);
+ when(storage1.isDefaultInternalStorage()).thenReturn(true);
+ final StorageEntry storage2 = mock(StorageEntry.class);
+ when(storage2.isDefaultInternalStorage()).thenReturn(false);
+
+ assertThat(storage1.compareTo(storage2) > 0).isTrue();
+ }
+
+ @Test
+ public void getDefaultInternalStorageEntry_shouldReturnVolumeInfoStorageOfIdPrivateInternal() {
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ when(mStorageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)).thenReturn(volumeInfo);
+
+ assertThat(StorageEntry.getDefaultInternalStorageEntry(mContext))
+ .isEqualTo(new StorageEntry(mContext, volumeInfo));
+ }
+
+ @Test
+ public void isVolumeInfo_shouldReturnTrueForVolumeInfo() {
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ final StorageEntry storage = new StorageEntry(mContext, volumeInfo);
+
+ assertThat(storage.isVolumeInfo()).isTrue();
+ assertThat(storage.isDiskInfoUnsupported()).isFalse();
+ assertThat(storage.isVolumeRecordMissed()).isFalse();
+ }
+
+ @Test
+ public void isDiskInfoUnsupported_shouldReturnTrueForDiskInfo() {
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry storage = new StorageEntry(diskInfo);
+
+ assertThat(storage.isVolumeInfo()).isFalse();
+ assertThat(storage.isDiskInfoUnsupported()).isTrue();
+ assertThat(storage.isVolumeRecordMissed()).isFalse();
+ }
+
+ @Test
+ public void isVolumeRecordMissed_shouldReturnTrueForVolumeRecord() {
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry storage = new StorageEntry(volumeRecord);
+
+ assertThat(storage.isVolumeInfo()).isFalse();
+ assertThat(storage.isDiskInfoUnsupported()).isFalse();
+ assertThat(storage.isVolumeRecordMissed()).isTrue();
+ }
+
+ @Test
+ public void isMounted_mountedOrMountedReadOnly_shouldReturnTrue() {
+ final VolumeInfo mountedVolumeInfo1 = mock(VolumeInfo.class);
+ final StorageEntry mountedStorage1 = new StorageEntry(mContext, mountedVolumeInfo1);
+ when(mountedVolumeInfo1.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+ final VolumeInfo mountedVolumeInfo2 = mock(VolumeInfo.class);
+ when(mountedVolumeInfo2.getState()).thenReturn(VolumeInfo.STATE_MOUNTED_READ_ONLY);
+ final StorageEntry mountedStorage2 = new StorageEntry(mContext, mountedVolumeInfo2);
+
+ assertThat(mountedStorage1.isMounted()).isTrue();
+ assertThat(mountedStorage2.isMounted()).isTrue();
+ }
+
+ @Test
+ public void isMounted_nonVolumeInfo_shouldReturnFalse() {
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+ assertThat(diskStorage.isMounted()).isFalse();
+ assertThat(recordStorage2.isMounted()).isFalse();
+ }
+
+ @Test
+ public void isUnmountable_unmountableVolume_shouldReturnTrue() {
+ final VolumeInfo unmountableVolumeInfo = mock(VolumeInfo.class);
+ final StorageEntry mountedStorage = new StorageEntry(mContext, unmountableVolumeInfo);
+ when(unmountableVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTABLE);
+
+ assertThat(mountedStorage.isUnmountable()).isTrue();
+ }
+
+ @Test
+ public void isUnmountable_nonVolumeInfo_shouldReturnFalse() {
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+ assertThat(diskStorage.isUnmountable()).isFalse();
+ assertThat(recordStorage2.isUnmountable()).isFalse();
+ }
+
+ @Test
+ public void isPrivate_privateVolume_shouldReturnTrue() {
+ final VolumeInfo privateVolumeInfo = mock(VolumeInfo.class);
+ final StorageEntry privateStorage = new StorageEntry(mContext, privateVolumeInfo);
+ when(privateVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+
+ assertThat(privateStorage.isPrivate()).isTrue();
+ }
+
+ @Test
+ public void isPrivate_nonVolumeInfo_shouldReturnFalse() {
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+ assertThat(diskStorage.isPrivate()).isFalse();
+ assertThat(recordStorage2.isPrivate()).isFalse();
+ }
+
+ @Test
+ public void getDescription_shouldReturnDescription() {
+ final String description = "description";
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ when(mStorageManager.getBestVolumeDescription(volumeInfo)).thenReturn(description);
+ final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ when(diskInfo.getDescription()).thenReturn(description);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+ when(volumeRecord.getNickname()).thenReturn(description);
+
+ assertThat(volumeStorage.getDescription()).isEqualTo(description);
+ assertThat(diskStorage.getDescription()).isEqualTo(description);
+ assertThat(recordStorage.getDescription()).isEqualTo(description);
+ }
+
+ @Test
+ public void getDiskId_shouldReturnDiskId() {
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+ when(volumeInfo.getDiskId()).thenReturn(VOLUME_INFO_ID);
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ when(diskInfo.getId()).thenReturn(DISK_INFO_ID);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+ assertThat(volumeStorage.getDiskId()).isEqualTo(VOLUME_INFO_ID);
+ assertThat(diskStorage.getDiskId()).isEqualTo(DISK_INFO_ID);
+ assertThat(recordStorage.getDiskId()).isEqualTo(null);
+ }
+
+ @Test
+ public void getFsUuid_shouldReturnFsUuid() {
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+ when(volumeInfo.getFsUuid()).thenReturn(VOLUME_INFO_ID);
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+ when(volumeRecord.getFsUuid()).thenReturn(VOLUME_RECORD_UUID);
+
+ assertThat(volumeStorage.getFsUuid()).isEqualTo(VOLUME_INFO_ID);
+ assertThat(diskStorage.getFsUuid()).isEqualTo(null);
+ assertThat(recordStorage.getFsUuid()).isEqualTo(VOLUME_RECORD_UUID);
+ }
+
+ @Test
+ public void getPath_shouldReturnPath() {
+ final File file = new File("fakePath");
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+ when(volumeInfo.getPath()).thenReturn(file);
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+ assertThat(volumeStorage.getPath()).isEqualTo(file);
+ assertThat(diskStorage.getPath()).isEqualTo(null);
+ assertThat(recordStorage.getPath()).isEqualTo(null);
+ }
+
+ @Test
+ public void getVolumeInfo_shouldVolumeInfo() {
+ final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+ final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+ final DiskInfo diskInfo = mock(DiskInfo.class);
+ final StorageEntry diskStorage = new StorageEntry(diskInfo);
+ final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+ final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+ assertThat(volumeStorage.getVolumeInfo()).isEqualTo(volumeInfo);
+ assertThat(diskStorage.getVolumeInfo()).isEqualTo(null);
+ assertThat(recordStorage.getVolumeInfo()).isEqualTo(null);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java
new file mode 100644
index 0000000..86351cb
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.storage.StorageManager;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.widget.SettingsSpinnerPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageSelectionPreferenceControllerTest {
+
+ private static final String PREFERENCE_KEY = "preference_key";
+
+ private Context mContext;
+ private StorageManager mStorageManager;
+ private StorageSelectionPreferenceController mController;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mController = new StorageSelectionPreferenceController(mContext, PREFERENCE_KEY);
+ }
+
+ @Test
+ public void setStorageEntries_fromStorageManager_correctAdapterItems() {
+ final List<StorageEntry> storageEntries = mStorageManager.getVolumes().stream()
+ .map(volumeInfo -> new StorageEntry(mContext, volumeInfo))
+ .collect(Collectors.toList());
+
+ mController.setStorageEntries(storageEntries);
+
+ final int adapterItemCount = mController.mStorageAdapter.getCount();
+ assertThat(adapterItemCount).isEqualTo(storageEntries.size());
+ for (int i = 0; i < adapterItemCount; i++) {
+ assertThat(storageEntries.get(i).getDescription())
+ .isEqualTo(mController.mStorageAdapter.getItem(i).getDescription());
+ }
+ }
+
+ @Test
+ public void setSelectedStorageEntry_primaryStorage_correctSelectedAdapterItem() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ final PreferenceScreen preferenceScreen =
+ preferenceManager.createPreferenceScreen(mContext);
+ final SettingsSpinnerPreference spinnerPreference = new SettingsSpinnerPreference(mContext);
+ spinnerPreference.setKey(PREFERENCE_KEY);
+ preferenceScreen.addPreference(spinnerPreference);
+ mController.displayPreference(preferenceScreen);
+ final StorageEntry primaryStorageEntry =
+ StorageEntry.getDefaultInternalStorageEntry(mContext);
+ mController.setStorageEntries(mStorageManager.getVolumes().stream()
+ .map(volumeInfo -> new StorageEntry(mContext, volumeInfo))
+ .collect(Collectors.toList()));
+
+ mController.setSelectedStorageEntry(primaryStorageEntry);
+
+ assertThat((StorageEntry) mController.mSpinnerPreference.getSelectedItem())
+ .isEqualTo(primaryStorageEntry);
+ }
+}
+
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java
new file mode 100644
index 0000000..6d9155a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.widget.UsageProgressBarPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageUsageProgressBarPreferenceControllerTest {
+
+ private static final String FAKE_UUID = "95D9-B3A4";
+ private static final long WAIT_TIMEOUT = 10_000L;
+ private static final long FREE_BYTES = 123L;
+ private static final long TOTAL_BYTES = 456L;
+ private static final long USAGE_BYTES = TOTAL_BYTES - FREE_BYTES;
+
+ private Context mContext;
+ private FakeStorageUsageProgressBarPreferenceController mController;
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private StorageStatsManager mStorageStatsManager;
+
+ @Before
+ public void setUp() throws Exception {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(StorageStatsManager.class)).thenReturn(mStorageStatsManager);
+ mController = new FakeStorageUsageProgressBarPreferenceController(mContext, "key");
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+ final UsageProgressBarPreference usageProgressBarPreference =
+ new UsageProgressBarPreference(mContext);
+ usageProgressBarPreference.setKey(mController.getPreferenceKey());
+ mPreferenceScreen.addPreference(usageProgressBarPreference);
+ }
+
+ @Test
+ public void setSelectedStorageEntry_primaryStorage_getPrimaryStorageBytes() throws IOException {
+ final StorageEntry defaultInternalStorageEntry =
+ StorageEntry.getDefaultInternalStorageEntry(mContext);
+ when(mStorageStatsManager.getTotalBytes(defaultInternalStorageEntry.getFsUuid()))
+ .thenReturn(TOTAL_BYTES);
+ when(mStorageStatsManager.getFreeBytes(defaultInternalStorageEntry.getFsUuid()))
+ .thenReturn(FREE_BYTES);
+ mController.displayPreference(mPreferenceScreen);
+
+ synchronized (mController.mLock) {
+ mController.setSelectedStorageEntry(defaultInternalStorageEntry);
+ mController.waitUpdateState(WAIT_TIMEOUT);
+ }
+
+ assertThat(mController.mUsedBytes).isEqualTo(USAGE_BYTES);
+ assertThat(mController.mTotalBytes).isEqualTo(TOTAL_BYTES);
+ }
+
+ private class FakeStorageUsageProgressBarPreferenceController
+ extends StorageUsageProgressBarPreferenceController {
+ private final Object mLock = new Object();
+
+ FakeStorageUsageProgressBarPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ try {
+ mLock.notifyAll();
+ } catch (IllegalMonitorStateException e) {
+ // Catch it for displayPreference to prevent exception by object not locked by
+ // thread before notify. Do nothing.
+ }
+ }
+
+ public void waitUpdateState(long timeout) {
+ try {
+ mLock.wait(timeout);
+ } catch (InterruptedException e) {
+ // Do nothing.
+ }
+ }
+ }
+}
+
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/VolumeSizesLoaderTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/VolumeSizesLoaderTest.java
index 79c5db8..77fd963 100644
--- a/tests/unit/src/com/android/settings/deviceinfo/storage/VolumeSizesLoaderTest.java
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/VolumeSizesLoaderTest.java
@@ -34,8 +34,10 @@
@RunWith(AndroidJUnit4.class)
public class VolumeSizesLoaderTest {
@Test
- public void getVolumeSize_getsValidSizes() throws Exception {
+ public void getVolumeSize_privateMountedVolume_getsValidSizes() throws Exception {
VolumeInfo info = mock(VolumeInfo.class);
+ when(info.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(info.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
StorageVolumeProvider storageVolumeProvider = mock(StorageVolumeProvider.class);
when(storageVolumeProvider.getTotalBytes(any(), any())).thenReturn(10000L);
when(storageVolumeProvider.getFreeBytes(any(), any())).thenReturn(1000L);
@@ -46,4 +48,19 @@
assertThat(storageInfo.freeBytes).isEqualTo(1000L);
assertThat(storageInfo.totalBytes).isEqualTo(10000L);
}
+
+ @Test
+ public void getVolumeSize_unmountedVolume_getsValidSizes() throws Exception {
+ VolumeInfo info = mock(VolumeInfo.class);
+ when(info.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTED);
+ StorageVolumeProvider storageVolumeProvider = mock(StorageVolumeProvider.class);
+ when(storageVolumeProvider.getTotalBytes(any(), any())).thenReturn(10000L);
+ when(storageVolumeProvider.getFreeBytes(any(), any())).thenReturn(1000L);
+
+ PrivateStorageInfo storageInfo =
+ VolumeSizesLoader.getVolumeSize(storageVolumeProvider, null, info);
+
+ assertThat(storageInfo.freeBytes).isEqualTo(0L);
+ assertThat(storageInfo.totalBytes).isEqualTo(0L);
+ }
}
diff --git a/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java b/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
index 05b92a8..6ddef5d 100644
--- a/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
@@ -45,7 +45,7 @@
}
@Test
- public void getAvailabilityStatus_byDefault_true() {
+ public void getAvailabilityStatus_returnAVAILABLE_UNSEARCHABLE() {
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE_UNSEARCHABLE);
}
diff --git a/tests/unit/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
index c540512..be3815d 100644
--- a/tests/unit/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
@@ -25,7 +25,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.net.ConnectivityManager;
import android.os.Looper;
import android.os.UserManager;
import android.provider.Settings;
@@ -63,8 +62,6 @@
@Mock
private UserManager mUserManager;
- @Mock
- private ConnectivityManager mConnectivityManager;
private PreferenceManager mPreferenceManager;
private PreferenceScreen mScreen;
@@ -82,7 +79,6 @@
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
- when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
if (Looper.myLooper() == null) {
Looper.prepare();
}
@@ -98,8 +94,7 @@
@Test
public void secondaryUser_prefIsNotAvailable() {
when(mUserManager.isAdminUser()).thenReturn(false);
- when(mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE))
- .thenReturn(true);
+ when(mTelephonyManager.isDataCapable()).thenReturn(true);
mController = new MobileNetworkPreferenceController(mContext);
assertThat(mController.isAvailable()).isFalse();
@@ -108,8 +103,7 @@
@Test
public void wifiOnly_prefIsNotAvailable() {
when(mUserManager.isAdminUser()).thenReturn(true);
- when(mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE))
- .thenReturn(false);
+ when(mTelephonyManager.isDataCapable()).thenReturn(false);
mController = new MobileNetworkPreferenceController(mContext);
assertThat(mController.isAvailable()).isFalse();
@@ -124,13 +118,12 @@
mLifecycleRegistry.handleLifecycleEvent(Event.ON_START);
verify(mController).onStart();
- verify(mTelephonyManager).listen(mController.mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ verify(mTelephonyManager).registerTelephonyCallback(
+ mContext.getMainExecutor(), mController.mTelephonyCallback);
mLifecycleRegistry.handleLifecycleEvent(Event.ON_STOP);
verify(mController).onStop();
- verify(mTelephonyManager).listen(mController.mPhoneStateListener,
- PhoneStateListener.LISTEN_NONE);
+ verify(mTelephonyManager).unregisterTelephonyCallback(mController.mTelephonyCallback);
}
@Test
@@ -148,12 +141,12 @@
mController.displayPreference(mScreen);
mLifecycleRegistry.handleLifecycleEvent(Event.ON_START);
verify(mController).onStart();
- verify(mTelephonyManager).listen(mController.mPhoneStateListener,
- PhoneStateListener.LISTEN_SERVICE_STATE);
+ verify(mTelephonyManager).registerTelephonyCallback(
+ mContext.getMainExecutor(), mController.mTelephonyCallback);
doReturn(testCarrierName).when(mController).getSummary();
- mController.mPhoneStateListener.onServiceStateChanged(null);
+ mController.mTelephonyCallback.onServiceStateChanged(null);
// Carrier name should be set.
Assert.assertEquals(mPreference.getSummary(), testCarrierName);
diff --git a/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java b/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
index cdb82a6..c51bb18 100644
--- a/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/MultiNetworkHeaderControllerTest.java
@@ -41,7 +41,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
@RunWith(AndroidJUnit4.class)
public class MultiNetworkHeaderControllerTest {
private static final String KEY_HEADER = "multi_network_header";
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
index d205607..ac2e24d 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
@@ -34,7 +34,6 @@
import android.net.NetworkCapabilities;
import android.net.Uri;
import android.os.PersistableBundle;
-import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
@@ -43,8 +42,6 @@
import android.text.Html;
import androidx.slice.Slice;
-import androidx.slice.builders.GridRowBuilder;
-import androidx.slice.builders.GridRowBuilder.CellBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -115,20 +112,6 @@
}
@Test
- public void createMessageGridRow_inputTheResourceId_verifyTitle() {
- int messageResId = ResourcesUtils.getResourcesId(mContext, "string",
- "non_carrier_network_unavailable");
- CharSequence title = ResourcesUtils.getResourcesString(mContext,
- "non_carrier_network_unavailable");
-
- GridRowBuilder testGridRow = mProviderModelSliceHelper.createMessageGridRow(messageResId,
- Settings.ACTION_AIRPLANE_MODE_SETTINGS);
- List<CellBuilder> cellItem = testGridRow.getCells();
-
- assertThat(cellItem.get(0).getTitle()).isEqualTo(title);
- }
-
- @Test
public void getConnectedWifiItem_inputListInvolveOneConnectedWifiItem_verifyReturnItem() {
when(mWifiSliceItem1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
when(mWifiSliceItem2.getConnectedState()).thenReturn(
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
index 705f60e..4760daa 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
@@ -22,7 +22,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -42,7 +41,6 @@
import androidx.slice.Slice;
import androidx.slice.SliceProvider;
-import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.widget.SliceLiveData;
@@ -97,12 +95,6 @@
private WifiSliceItem mMockWifiSliceItem3;
@Mock
ListBuilder.RowBuilder mMockCarrierRowBuild;
- @Mock
- ListBuilder.HeaderBuilder mMockHeader;
- @Mock
- GridRowBuilder mMockGridRowBuilderNonCarrierNetworkUnavailable;
- @Mock
- GridRowBuilder mMockGridRowBuilderAllNetworkUnavailable;
private FakeFeatureFactory mFeatureFactory;
@Mock
@@ -147,35 +139,7 @@
@Test
@UiThreadTest
- public void getSlice_noWorkerAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
- mWifiList.clear();
- mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
- mockHelperCondition(false, false, false, null);
-
- final Slice slice = mMockProviderModelSlice.getSlice();
-
- assertThat(slice).isNotNull();
- verify(mListBuilder, times(1)).setHeader(mMockHeader);
- verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
- }
-
- @Test
- @UiThreadTest
- public void getSlice_noWifiAndNoCarrier_getOneHeaderOneGridRowWithAllNetworkUnavailable() {
- mWifiList.clear();
- mMockNetworkProviderWorker.updateSelfResults(null);
- mockHelperCondition(false, false, false, null);
-
- final Slice slice = mMockProviderModelSlice.getSlice();
-
- assertThat(slice).isNotNull();
- verify(mListBuilder, times(1)).setHeader(mMockHeader);
- verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
- }
-
- @Test
- @UiThreadTest
- public void getSlice_noWifiAndHasCarrierNoData_oneCarrierOneGridRowWithAllNetworkUnavailable() {
+ public void getSlice_noWifiAndHasCarrierNoData_oneCarrier() {
mWifiList.clear();
mMockNetworkProviderWorker.updateSelfResults(null);
mockHelperCondition(false, true, false, null);
@@ -184,12 +148,11 @@
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
- verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderAllNetworkUnavailable);
}
@Test
@UiThreadTest
- public void getSlice_noWifiAndNoCarrier_oneCarrierOneGridRowWithNonCarrierNetworkUnavailable() {
+ public void getSlice_noWifiAndNoCarrier_oneCarrier() {
mWifiList.clear();
mMockProviderModelSlice = new MockProviderModelSlice(mContext, null);
mockHelperCondition(false, true, true, null);
@@ -198,7 +161,6 @@
assertThat(slice).isNotNull();
verify(mListBuilder, times(1)).addRow(mMockCarrierRowBuild);
- verify(mListBuilder, times(1)).addGridRow(mMockGridRowBuilderNonCarrierNetworkUnavailable);
}
@Test
@@ -331,19 +293,6 @@
private void mockBuilder() {
SliceAction mockSliceAction = getPrimarySliceAction();
- when(mMockHeader.getTitle()).thenReturn("mockHeader");
- when(mMockHeader.getPrimaryAction()).thenReturn(mockSliceAction);
- when(mProviderModelSliceHelper.createHeader(anyString())).thenReturn(mMockHeader);
-
- int resId = ResourcesUtils.getResourcesId(mContext, "string",
- "non_carrier_network_unavailable");
- when(mProviderModelSliceHelper.createMessageGridRow(eq(resId), anyString())).thenReturn(
- mMockGridRowBuilderNonCarrierNetworkUnavailable);
- resId = ResourcesUtils.getResourcesId(mContext, "string",
- "all_network_unavailable");
- when(mProviderModelSliceHelper.createMessageGridRow(eq(resId), anyString())).thenReturn(
- mMockGridRowBuilderAllNetworkUnavailable);
-
when(mMockCarrierRowBuild.getTitle()).thenReturn("mockRow");
when(mMockCarrierRowBuild.getPrimaryAction()).thenReturn(mockSliceAction);
when(mProviderModelSliceHelper.createCarrierRow(anyString())).thenReturn(
diff --git a/tests/unit/src/com/android/settings/network/telephony/EuiccPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/EuiccPreferenceControllerTest.java
index f1ad2ff..ccc12e3 100644
--- a/tests/unit/src/com/android/settings/network/telephony/EuiccPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/EuiccPreferenceControllerTest.java
@@ -38,7 +38,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
@RunWith(AndroidJUnit4.class)
public class EuiccPreferenceControllerTest {
private static final int SUB_ID = 2;
diff --git a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
index cf4eb91..45f3693 100644
--- a/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/MobileDataPreferenceControllerTest.java
@@ -47,7 +47,6 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-
@RunWith(AndroidJUnit4.class)
public class MobileDataPreferenceControllerTest {
private static final int SUB_ID = 2;
diff --git a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java
index ba5ee8e..8f0cfb3 100644
--- a/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java
+++ b/tests/unit/src/com/android/settings/panel/InternetConnectivityPanelTest.java
@@ -22,15 +22,19 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.Uri;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.network.AirplaneModePreferenceController;
import com.android.settings.network.InternetUpdater;
+import com.android.settings.network.ProviderModelSliceHelper;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.testutils.ResourcesUtils;
@@ -42,6 +46,7 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
+import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@@ -55,6 +60,12 @@
ApplicationProvider.getApplicationContext(), "wifi_is_turned_on_subtitle");
public static final String BUTTON_SETTINGS = ResourcesUtils.getResourcesString(
ApplicationProvider.getApplicationContext(), "settings_button");
+ public static final String SUBTITLE_NON_CARRIER_NETWORK_UNAVAILABLE =
+ ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(),
+ "non_carrier_network_unavailable");
+ public static final String SUBTITLE_ALL_NETWORK_UNAVAILABLE =
+ ResourcesUtils.getResourcesString(ApplicationProvider.getApplicationContext(),
+ "all_network_unavailable");
@Rule
public final MockitoRule mMocks = MockitoJUnit.rule();
@@ -62,6 +73,10 @@
PanelContentCallback mPanelContentCallback;
@Mock
InternetUpdater mInternetUpdater;
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private ProviderModelSliceHelper mProviderModelSliceHelper;
private Context mContext;
private InternetConnectivityPanel mPanel;
@@ -69,11 +84,14 @@
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
mPanel = InternetConnectivityPanel.create(mContext);
mPanel.registerCallback(mPanelContentCallback);
mPanel.mIsProviderModelEnabled = true;
mPanel.mInternetUpdater = mInternetUpdater;
+ mPanel.mProviderModelSliceHelper = mProviderModelSliceHelper;
}
@Test
@@ -91,13 +109,6 @@
}
@Test
- public void getSubTitle_apmOff_shouldBeNull() {
- doReturn(false).when(mInternetUpdater).isAirplaneModeOn();
-
- assertThat(mPanel.getSubTitle()).isNull();
- }
-
- @Test
public void getSubTitle_apmOnWifiOff_shouldBeNull() {
doReturn(true).when(mInternetUpdater).isAirplaneModeOn();
doReturn(false).when(mInternetUpdater).isWifiEnabled();
@@ -110,10 +121,44 @@
doReturn(true).when(mInternetUpdater).isAirplaneModeOn();
doReturn(true).when(mInternetUpdater).isWifiEnabled();
+ mPanel.updatePanelTitle();
+
assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_WIFI_IS_TURNED_ON);
}
@Test
+ public void getSubTitle_apmOffWifiOnNoWifiListHasCarrierData_NonCarrierNetworkUnavailable() {
+ List wifiList = new ArrayList<ScanResult>();
+ mockCondition(false, true, true, true, wifiList);
+
+ mPanel.updatePanelTitle();
+
+ assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_NON_CARRIER_NETWORK_UNAVAILABLE);
+ }
+
+ @Test
+ public void getSubTitle_apmOffWifiOnNoWifiListNoCarrierData_AllNetworkUnavailable() {
+ List wifiList = new ArrayList<ScanResult>();
+ mockCondition(false, true, false, true, wifiList);
+
+ mPanel.updatePanelTitle();
+
+ assertThat(mPanel.getSubTitle()).isEqualTo(SUBTITLE_ALL_NETWORK_UNAVAILABLE);
+ }
+
+ @Test
+ public void getSubTitle_apmOffWifiOnTwoWifiItemsNoCarrierData_shouldBeNull() {
+ List wifiList = new ArrayList<ScanResult>();
+ wifiList.add(new ScanResult());
+ wifiList.add(new ScanResult());
+ mockCondition(false, true, false, true, wifiList);
+
+ mPanel.updatePanelTitle();
+
+ assertThat(mPanel.getSubTitle()).isNull();
+ }
+
+ @Test
public void getCustomizedButtonTitle_apmOff_shouldBeSettings() {
doReturn(false).when(mInternetUpdater).isAirplaneModeOn();
@@ -244,4 +289,13 @@
verify(mPanelContentCallback).onCustomizedButtonStateChanged();
}
+
+ private void mockCondition(boolean airplaneMode, boolean hasCarrier,
+ boolean isDataSimActive, boolean isWifiEnabled, List<ScanResult> wifiItems) {
+ doReturn(airplaneMode).when(mInternetUpdater).isAirplaneModeOn();
+ when(mProviderModelSliceHelper.hasCarrier()).thenReturn(hasCarrier);
+ when(mProviderModelSliceHelper.isDataSimActive()).thenReturn(isDataSimActive);
+ doReturn(isWifiEnabled).when(mInternetUpdater).isWifiEnabled();
+ doReturn(wifiItems).when(mWifiManager).getScanResults();
+ }
}
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java
new file mode 100644
index 0000000..a2b99bf
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 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.wifi.tether;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Looper;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class WifiTetherMaximizeCompatibilityPreferenceControllerTest {
+
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ private WifiManager mWifiManager;
+ @Mock
+ private WifiTetherBasePreferenceController.OnTetherConfigUpdateListener mListener;
+
+ private WifiTetherMaximizeCompatibilityPreferenceController mController;
+ private SwitchPreference mPreference;
+ private SoftApConfiguration mConfig;
+
+ @Before
+ public void setUp() {
+ final Context context = spy(ApplicationProvider.getApplicationContext());
+ mConfig = new SoftApConfiguration.Builder()
+ .setSsid("test_Ssid")
+ .setPassphrase(null, SoftApConfiguration.SECURITY_TYPE_OPEN)
+ .setBridgedModeOpportunisticShutdownEnabled(true)
+ .build();
+ doReturn(mWifiManager).when(context).getSystemService(Context.WIFI_SERVICE);
+ doReturn(true).when(mWifiManager).isBridgedApConcurrencySupported();
+ doReturn(mConfig).when(mWifiManager).getSoftApConfiguration();
+
+ mController = new WifiTetherMaximizeCompatibilityPreferenceController(context, mListener);
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ final PreferenceManager preferenceManager = new PreferenceManager(context);
+ final PreferenceScreen screen = preferenceManager.createPreferenceScreen(context);
+ mPreference = new SwitchPreference(context);
+ mPreference.setKey(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY);
+ screen.addPreference(mPreference);
+ mController.displayPreference(screen);
+ }
+
+ @Test
+ public void getPreferenceKey_shouldBeCorrect() {
+ assertThat(mController.getPreferenceKey())
+ .isEqualTo(WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY);
+ }
+
+ @Test
+ public void updateDisplay_notSupport5GHzBand_setPreferenceDisabled() {
+ doReturn(false).when(mWifiManager).is5GHzBandSupported();
+
+ mController.updateDisplay();
+
+ assertThat(mPreference.isEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void updateDisplay_getNullCountryCode_setPreferenceDisabled() {
+ doReturn(null).when(mWifiManager).getCountryCode();
+
+ mController.updateDisplay();
+
+ assertThat(mPreference.isEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void updateDisplay_supported5GHzBandAndCountryCodeIsNotNull_setPreferenceEnabled() {
+ doReturn(true).when(mWifiManager).is5GHzBandSupported();
+ doReturn("US").when(mWifiManager).getCountryCode();
+
+ mController.updateDisplay();
+
+ assertThat(mPreference.isEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void onPreferenceChange_callbackOnTetherConfigUpdated() {
+ mController.onPreferenceChange(mPreference, true);
+ verify(mListener).onTetherConfigUpdated(any());
+ }
+
+ @Test
+ public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnTure() {
+ // The preconditions are ready in setup().
+
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnFalse() {
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setBridgedModeOpportunisticShutdownEnabled(false)
+ .build();
+ doReturn(config).when(mWifiManager).getSoftApConfiguration();
+
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gOnly_returnFalse() {
+ doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported();
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setBand(SoftApConfiguration.BAND_2GHZ)
+ .build();
+ doReturn(config).when(mWifiManager).getSoftApConfiguration();
+
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand5gOnly_returnTrue() {
+ doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported();
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setBand(SoftApConfiguration.BAND_5GHZ)
+ .build();
+ doReturn(config).when(mWifiManager).getSoftApConfiguration();
+
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void isMaximizeCompatibilityEnabled_noConcurrencyAndGetBand2gAnd5g_returnTrue() {
+ doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported();
+ SoftApConfiguration config = new SoftApConfiguration.Builder()
+ .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ)
+ .build();
+ doReturn(config).when(mWifiManager).getSoftApConfiguration();
+
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setDisabled() {
+ // The precondition of the concurrency supported is ready in setup().
+ mController.onPreferenceChange(mPreference, false);
+
+ SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
+ mController.setupMaximizeCompatibility(builder);
+
+ assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(false);
+ }
+
+ @Test
+ public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setEnabled() {
+ // The precondition of the concurrency supported is ready in setup().
+ mController.onPreferenceChange(mPreference, true);
+
+ SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
+ mController.setupMaximizeCompatibility(builder);
+
+ assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(true);
+ }
+
+ @Test
+ public void setupMaximizeCompatibility_noConcurrencyAndSetDisabled_setBand2gOnly() {
+ doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported();
+ mController.onPreferenceChange(mPreference, false);
+
+ SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
+ mController.setupMaximizeCompatibility(builder);
+
+ assertThat(builder.build().getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
+ }
+
+ @Test
+ public void setupMaximizeCompatibility_noConcurrencyAndSetEnabled_setBand2gAnd5g() {
+ doReturn(false).when(mWifiManager).isBridgedApConcurrencySupported();
+ mController.onPreferenceChange(mPreference, true);
+
+ SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
+ mController.setupMaximizeCompatibility(builder);
+
+ assertThat(builder.build().getBand())
+ .isEqualTo(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ);
+ }
+}