Merge "Added fingerprint strings" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eeb9694..cb53a02 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.HARDWARE_TEST" />
@@ -105,6 +107,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_DREAM_STATE" />
<uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION" />
+ <uses-permission android:name="android.permission.MANAGE_APP_HIBERNATION" />
<application android:label="@string/settings_label"
android:icon="@drawable/ic_launcher_settings"
@@ -1231,7 +1234,6 @@
<activity android:name=".applications.InstalledAppOpenByDefaultActivity"
android:label="@string/application_info_label"
- android:permission="android.permission.OPEN_APP_OPEN_BY_DEFAULT_SETTINGS"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.android.settings.APP_OPEN_BY_DEFAULT_SETTINGS" />
@@ -1488,6 +1490,7 @@
<activity android:name=".applications.specialaccess.deviceadmin.DeviceAdminAdd"
android:label="@string/device_admin_add_title"
android:exported="true"
+ android:theme="@style/Theme.SubSettings"
android:clearTaskOnLaunch="true">
<intent-filter>
<action android:name="android.app.action.ADD_DEVICE_ADMIN" />
@@ -1969,7 +1972,7 @@
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
- android:value="com.android.settings.deviceinfo.StorageSettings" />
+ android:value="com.android.settings.deviceinfo.StorageDashboardFragment" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>
diff --git a/res/drawable/ic_files_go_round.xml b/res/drawable/ic_files_go_round.xml
new file mode 100644
index 0000000..2025b00
--- /dev/null
+++ b/res/drawable/ic_files_go_round.xml
@@ -0,0 +1,121 @@
+<!--
+ 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"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="192"
+ android:viewportHeight="192">
+ <path
+ android:pathData="M96,96m-88,0a88,88 0,1 1,176 0a88,88 0,1 1,-176 0"
+ android:fillColor="#FFFFFF"/>
+ <path
+ android:pathData="M96,183c-48.43,0 -87.72,-39.13 -87.99,-87.5C8.01,95.67 8,95.83 8,96c0,48.6 39.4,88 88,88s88,-39.4 88,-88c0,-0.17 -0.01,-0.33 -0.01,-0.5C183.72,143.87 144.43,183 96,183z"
+ android:fillColor="#263238"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M96,9c48.43,0 87.72,39.13 87.99,87.5c0,-0.17 0.01,-0.33 0.01,-0.5c0,-48.6 -39.4,-88 -88,-88S8,47.4 8,96c0,0.17 0.01,0.33 0.01,0.5C8.28,48.13 47.57,9 96,9z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M85.09,59.18H44.18c-4.52,0 -8.18,3.66 -8.18,8.18v40.91v2.05v-0.35c0,-1.08 0.43,-2.13 1.2,-2.89L85.09,59.18z"
+ android:fillColor="#0F9D58"/>
+ <path
+ android:pathData="M85.09,59.18H44.18c-4.52,0 -8.18,3.66 -8.18,8.18v40.91v2.05v-0.35c0,-1.08 0.43,-2.13 1.2,-2.89L85.09,59.18z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:startY="110.3181"
+ android:startX="60.5458"
+ android:endY="59.182"
+ android:endX="60.5458"
+ android:type="linear">
+ <item android:offset="0" android:color="#33263238"/>
+ <item android:offset="1" android:color="#00263238"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:pathData="M37.2,106.39c-0.77,0.77 -1.2,1.81 -1.2,2.89v0.35v0.33c0,-1.08 0.43,-2.13 1.2,-2.89l47.89,-47.89h-0.68L37.2,106.39z"
+ android:fillColor="#263238"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M44.18,59.86h39.55l0.68,-0.68H44.18c-4.52,0 -8.18,3.66 -8.18,8.18v0.68C36,63.53 39.66,59.86 44.18,59.86z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M147.82,143.73H44.18c-4.52,0 -8.18,-3.66 -8.18,-8.18v-25.58c0,-1.08 0.43,-2.13 1.2,-2.89l57.6,-57.6c0.77,-0.77 1.81,-1.2 2.89,-1.2h50.12c4.52,0 8.18,3.66 8.18,8.18v79.09C156,140.06 152.34,143.73 147.82,143.73z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M156,135.55l0,-29.32L98.05,48.27l-0.31,0c-1.11,0 -2.17,0.45 -2.94,1.25L37.2,107.07c-0.77,0.77 -1.2,1.81 -1.2,2.89v0.35l33.41,33.41l78.41,0C152.34,143.73 156,140.06 156,135.55z">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:startY="78.2732"
+ android:startX="66.0008"
+ android:endY="153.6034"
+ android:endX="141.331"
+ android:type="linear">
+ <item android:offset="0" android:color="#281A237E"/>
+ <item android:offset="1" android:color="#051A237E"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:pathData="M147.82,143.05H44.18c-4.52,0 -8.18,-3.66 -8.18,-8.18v0.68c0,4.52 3.66,8.18 8.18,8.18h103.64c4.52,0 8.18,-3.66 8.18,-8.18v-0.68C156,139.38 152.34,143.05 147.82,143.05z"
+ android:fillColor="#1A237E"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M147.82,48.27H98.05c-0.6,0 -1.14,0.27 -1.51,0.68h51.29c4.52,0 8.18,3.66 8.18,8.18v-0.68C156,51.94 152.34,48.27 147.82,48.27z"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M94.8,49.47l-57.6,57.6c-0.77,0.77 -1.2,1.81 -1.2,2.89v0.35v0c0,-1.13 0.92,-2.05 2.05,-2.05h49.77c4.52,0 8.18,-3.66 8.18,-8.18l0,-49.77c0,-1.13 0.92,-2.05 2.05,-2.05l0,0l-0.35,0C96.61,48.27 95.57,48.7 94.8,49.47z"
+ android:fillColor="#DB4437"/>
+ <path
+ android:pathData="M97.69,48.27c-0.27,0 -0.54,0.03 -0.8,0.08C97.15,48.3 97.42,48.27 97.69,48.27l0.35,0c-1.34,0 -2.55,0.72 -3.21,1.85c0.43,-0.42 0.95,-0.73 1.5,-0.93c0.37,-0.55 0.99,-0.92 1.7,-0.92L97.69,48.27z"
+ android:strokeAlpha="0.2"
+ android:fillColor="#3E2723"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M96,99.41c0,4.52 -3.66,8.18 -8.18,8.18H38.05c-0.85,0 -1.57,0.51 -1.88,1.25c-0.1,0.36 -0.16,0.74 -0.16,1.13v0.35c0,-1.13 0.92,-2.05 2.05,-2.05h49.77c4.52,0 8.18,-3.66 8.18,-8.18"
+ android:fillColor="#3E2723"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M36,110.32L36,110.32c0,-1.62 1.05,-3.06 2.6,-3.55L74.95,95.2c3.79,-1.21 6.77,-4.18 7.97,-7.97l11.57,-36.35c0.49,-1.55 1.93,-2.6 3.55,-2.6l0,0h-0.35c-1.08,0 -2.13,0.43 -2.89,1.2l-57.6,57.6c-0.77,0.77 -1.2,1.81 -1.2,2.89L36,110.32z"
+ android:fillColor="#F4B400"/>
+ <path
+ android:pathData="M94.84,49.44c-0.14,0.24 -0.26,0.48 -0.35,0.75L82.92,86.54c-1.21,3.79 -4.18,6.77 -7.97,7.97L38.6,106.08c-0.27,0.09 -0.52,0.21 -0.76,0.35l-0.64,0.64c-0.77,0.77 -1.2,1.81 -1.2,2.89l0,0.35c0,-1.62 1.05,-3.06 2.6,-3.55L74.95,95.2c3.79,-1.21 6.77,-4.18 7.97,-7.97l11.57,-36.35c0.49,-1.55 1.93,-2.6 3.55,-2.6h-0.35C96.63,48.27 95.6,48.69 94.84,49.44z"
+ android:fillColor="#BF360C"
+ android:fillAlpha="0.3"/>
+ <path
+ android:pathData="M39,105.95l55.36,-55.36l0.13,-0.4c0.09,-0.27 0.21,-0.52 0.35,-0.75c-0.01,0.01 -0.03,0.02 -0.04,0.03l-56.96,56.96c0.24,-0.14 0.49,-0.27 0.76,-0.35L39,105.95z"
+ android:strokeAlpha="0.2"
+ android:fillColor="#FFFFFF"
+ android:fillAlpha="0.2"/>
+ <path
+ android:pathData="M36,109.97v25.58c0,4.52 3.66,8.18 8.18,8.18h103.64c4.52,0 8.18,-3.66 8.18,-8.18V56.45c0,-4.52 -3.66,-8.18 -8.18,-8.18H97.69c-1.08,0 -2.13,0.43 -2.89,1.2l-9.71,9.71H44.18c-4.52,0 -8.18,3.66 -8.18,8.18v40.91L36,109.97">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:gradientRadius="115.3882"
+ android:centerX="72.364"
+ android:centerY="59.4093"
+ android:type="radial">
+ <item android:offset="0.0029046" android:color="#19FFFFFF"/>
+ <item android:offset="1" android:color="#00FFFFFF"/>
+ </gradient>
+ </aapt:attr>
+ </path>
+</vector>
diff --git a/res/drawable/ic_homepage_search.xml b/res/drawable/ic_homepage_search.xml
index 3895b6b..3da1cc7 100644
--- a/res/drawable/ic_homepage_search.xml
+++ b/res/drawable/ic_homepage_search.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
+ android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="#FF000000"
android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/>
diff --git a/res/drawable/ic_refresh_24dp.xml b/res/drawable/ic_refresh_24dp.xml
deleted file mode 100644
index 9b78fcd..0000000
--- a/res/drawable/ic_refresh_24dp.xml
+++ /dev/null
@@ -1,26 +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.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:tint="?android:attr/colorAccent">
- <path
- android:fillColor="@android:color/white"
- android:pathData="M13,9v2h7V4h-2v2.74C16.53,5.07 14.4,4 12,4c-2.21,0 -4.21,0.9 -5.66,2.34S4,9.79 4,12c0,4.42 3.58,8 8,8 2.21,0 4.21,-0.9 5.66,-2.34l-1.42,-1.42C15.15,17.33 13.65,18 12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.65 0.67,-3.15 1.76,-4.24C8.85,6.67 10.35,6 12,6c2.21,0 4.15,1.21 5.19,3H13z"/>
-</vector>
diff --git a/res/drawable/ic_repair_24dp.xml b/res/drawable/ic_repair_24dp.xml
new file mode 100644
index 0000000..da351fc
--- /dev/null
+++ b/res/drawable/ic_repair_24dp.xml
@@ -0,0 +1,29 @@
+<?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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M12,5c3.53,0 6.44,2.61 6.93,6h2.02c-0.5,-4.5 -4.31,-8 -8.95,-8c-4.97,0 -9,4.03 -9,9c0,3.48 1.98,6.5 4.87,8H5v2h6v-6H9v2.33C6.64,17.2 5,14.79 5,12C5,8.13 8.13,5 12,5z"/>
+ <path
+ android:fillColor="@android:color/black"
+ android:pathData="M22.16,16.5c0,1.93 -1.57,3.5 -3.5,3.5c-0.41,0 -0.8,-0.08 -1.16,-0.21l-2.34,2.33L13.04,20l2.33,-2.34c-0.13,-0.36 -0.21,-0.75 -0.21,-1.16c0,-1.93 1.57,-3.5 3.5,-3.5c0.58,0 1.12,0.16 1.6,0.41l-2.7,2.7l1.49,1.49l2.7,-2.7C22,15.38 22.16,15.92 22.16,16.5z"/>
+</vector>
diff --git a/res/drawable/ic_trash_can.xml b/res/drawable/ic_trash_can.xml
new file mode 100644
index 0000000..ed4a4cb
--- /dev/null
+++ b/res/drawable/ic_trash_can.xml
@@ -0,0 +1,26 @@
+<!--
+ 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"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M15,3V4H20V6H19V19C19,20.1 18.1,21 17,21H7C5.9,21 5,20.1 5,19V6H4V4H9V3H15ZM7,19H17V6H7V19ZM9,8H11V17H9V8ZM15,8H13V17H15V8Z"
+ android:fillColor="#5F6368"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/res/drawable/ic_warning_circle_24dp.xml b/res/drawable/ic_warning_circle_24dp.xml
new file mode 100644
index 0000000..54e2ab4
--- /dev/null
+++ b/res/drawable/ic_warning_circle_24dp.xml
@@ -0,0 +1,26 @@
+<?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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="?android:attr/colorError"
+ android:pathData="M11,15h2v2h-2v-2zM11,7h2v6h-2L11,7zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
+</vector>
diff --git a/res/layout-land/confirm_lock_pattern.xml b/res/layout-land/confirm_lock_pattern.xml
index 1fd6824..1126052 100644
--- a/res/layout-land/confirm_lock_pattern.xml
+++ b/res/layout-land/confirm_lock_pattern.xml
@@ -48,13 +48,13 @@
android:src="@drawable/ic_enterprise" />
<TextView
- android:id="@+id/headerText"
+ android:id="@+id/suc_layout_title"
style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
- android:id="@+id/sud_layout_description"
+ android:id="@+id/sud_layout_subtitle"
style="@style/SudDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout-land/confirm_lock_pattern_normal.xml b/res/layout-land/confirm_lock_pattern_normal.xml
index 7a59894..2a9103f 100644
--- a/res/layout-land/confirm_lock_pattern_normal.xml
+++ b/res/layout-land/confirm_lock_pattern_normal.xml
@@ -48,14 +48,14 @@
android:src="@drawable/ic_lock" />
<TextView
- android:id="@+id/headerText"
+ android:id="@+id/suc_layout_title"
style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
style="@style/SudDescription.Glif"
- android:id="@+id/sud_layout_description"
+ android:id="@+id/sud_layout_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginStart"
diff --git a/res/layout-land/udfps_enroll_enrolling_land.xml b/res/layout-land/udfps_enroll_enrolling_land.xml
new file mode 100644
index 0000000..7ccd396
--- /dev/null
+++ b/res/layout-land/udfps_enroll_enrolling_land.xml
@@ -0,0 +1,95 @@
+<?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.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/setup_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/sud_glif_blank_template"
+ style="?attr/fingerprint_layout_theme">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
+ See layouts/fingerprint_enroll_enrolling_base.xml. -->
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:layout_marginStart="?attr/sudMarginStart"
+ android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <ImageView
+ android:id="@+id/sud_layout_icon"
+ style="@style/SudGlifIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="?attr/sudGlifHeaderGravity"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="0dp"
+ android:src="@drawable/ic_fingerprint_header" />
+
+ <TextView
+ android:id="@+id/suc_layout_title"
+ style="@style/SudGlifHeaderTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="0dp" />
+
+ <TextView
+ style="@style/SudDescription.Glif"
+ android:id="@+id/sud_layout_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ </LinearLayout>
+
+ </LinearLayout>
+</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout-land/udfps_enroll_find_sensor_land.xml b/res/layout-land/udfps_enroll_find_sensor_land.xml
new file mode 100644
index 0000000..6335afb
--- /dev/null
+++ b/res/layout-land/udfps_enroll_find_sensor_land.xml
@@ -0,0 +1,94 @@
+<?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.google.android.setupdesign.GlifLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/setup_wizard_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout="@layout/sud_glif_blank_template"
+ style="?attr/fingerprint_layout_theme">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_height="match_parent"
+ android:layout_marginStart="?attr/sudMarginStart"
+ android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:orientation="vertical">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <ImageView
+ android:id="@+id/sud_layout_icon"
+ style="@style/SudGlifIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="?attr/sudGlifHeaderGravity"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="0dp"
+ android:src="@drawable/ic_fingerprint_header" />
+
+ <TextView
+ android:id="@+id/suc_layout_title"
+ style="@style/SudGlifHeaderTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="0dp" />
+
+ <TextView
+ android:id="@+id/sud_layout_subtitle"
+ style="@style/SudDescription.Glif"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/app_authentication_item.xml b/res/layout/app_authentication_item.xml
index 423722e..16d6fbd 100644
--- a/res/layout/app_authentication_item.xml
+++ b/res/layout/app_authentication_item.xml
@@ -26,7 +26,8 @@
android:layout_width="24dp"
android:layout_height="24dp"/>
- <LinearLayout
+ <RelativeLayout
+ android:id="@+id/app_details"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/app_icon"
@@ -44,16 +45,18 @@
<TextView
android:id="@+id/number_of_uris"
style="@style/AppAuthenticationPolicyNumberOfUrisText"
+ android:layout_below="@id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/uris"
+ android:layout_below="@id/app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
- </LinearLayout>
+ </RelativeLayout>
<ImageView
android:id="@+id/expand"
diff --git a/res/layout/app_launch_progress.xml b/res/layout/app_launch_progress.xml
new file mode 100644
index 0000000..28fe406
--- /dev/null
+++ b/res/layout/app_launch_progress.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<ProgressBar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@android:style/Widget.Material.ProgressBar.Horizontal"
+ android:id="@+id/scan_links_progressbar"
+ android:paddingTop="8dp"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
\ No newline at end of file
diff --git a/res/layout/app_launch_verified_links_title.xml b/res/layout/app_launch_verified_links_title.xml
new file mode 100644
index 0000000..a230edc
--- /dev/null
+++ b/res/layout/app_launch_verified_links_title.xml
@@ -0,0 +1,49 @@
+<?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:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:paddingTop="@*android:dimen/dialog_padding_top_material">
+
+ <TextView
+ android:id="@+id/dialog_title"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="center"
+ style="?android:attr/windowTitleStyle" />
+
+ <TextView
+ android:paddingTop="12dp"
+ android:id="@+id/dialog_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.DialogMessage" />
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/confirm_lock_pattern_base.xml b/res/layout/confirm_lock_pattern_base.xml
index 895bb8a..bac2689 100644
--- a/res/layout/confirm_lock_pattern_base.xml
+++ b/res/layout/confirm_lock_pattern_base.xml
@@ -42,13 +42,13 @@
android:src="@drawable/ic_enterprise" />
<TextView
- android:id="@+id/headerText"
+ android:id="@+id/suc_layout_title"
style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
- android:id="@+id/sud_layout_description"
+ android:id="@+id/sud_layout_subtitle"
style="@style/SudDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/layout/confirm_lock_pattern_normal_base.xml b/res/layout/confirm_lock_pattern_normal_base.xml
index 7c1502d..29783dd 100644
--- a/res/layout/confirm_lock_pattern_normal_base.xml
+++ b/res/layout/confirm_lock_pattern_normal_base.xml
@@ -41,14 +41,14 @@
android:src="@drawable/ic_lock" />
<TextView
- android:id="@+id/headerText"
+ android:id="@+id/suc_layout_title"
style="@style/SudGlifHeaderTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
style="@style/SudDescription.Glif"
- android:id="@+id/sud_layout_description"
+ android:id="@+id/sud_layout_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="?attr/sudMarginStart"
diff --git a/res/layout/horizontal_preference.xml b/res/layout/horizontal_preference.xml
index 9358cff..e50fc3f 100644
--- a/res/layout/horizontal_preference.xml
+++ b/res/layout/horizontal_preference.xml
@@ -22,7 +22,7 @@
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:orientation="horizontal"
- android:paddingStart="@dimen/preference_no_icon_padding_start"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
diff --git a/res/layout/more_settings_button.xml b/res/layout/more_settings_button.xml
new file mode 100644
index 0000000..4bbe119
--- /dev/null
+++ b/res/layout/more_settings_button.xml
@@ -0,0 +1,35 @@
+<?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"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="20dip"
+ android:layout_marginBottom="20dip"
+ style="@style/ActionPrimaryButton"
+ settings:allowDividerBelow="true"
+ android:gravity="center" />
+
+</LinearLayout>
diff --git a/res/layout/one_handed_header.xml b/res/layout/one_handed_header.xml
index 2104e40..3fa8183 100644
--- a/res/layout/one_handed_header.xml
+++ b/res/layout/one_handed_header.xml
@@ -21,7 +21,7 @@
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:scaleType="centerCrop"
+ android:scaleType="centerInside"
android:cropToPadding="true"
android:src="@drawable/one_handed_guideline"
android:contentDescription="@null" />
diff --git a/res/layout/preference_widget_info.xml b/res/layout/preference_widget_info.xml
new file mode 100644
index 0000000..39c2aca
--- /dev/null
+++ b/res/layout/preference_widget_info.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ android:src="@drawable/ic_info_outline_24"
+ android:contentDescription="@string/settings_button" />
\ No newline at end of file
diff --git a/res/layout/preference_widget_warning.xml b/res/layout/preference_widget_warning.xml
new file mode 100644
index 0000000..ea54afe
--- /dev/null
+++ b/res/layout/preference_widget_warning.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<!-- Warning button -->
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/warning_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:paddingStart="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:scaleType="center"
+ android:src="@drawable/ic_warning_circle_24dp"
+ android:contentDescription="@string/warning_button_text" />
diff --git a/res/layout/search_bar.xml b/res/layout/search_bar.xml
index 63f1c95..7cdf04d 100644
--- a/res/layout/search_bar.xml
+++ b/res/layout/search_bar.xml
@@ -19,10 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/search_bar_margin"
- android:layout_marginStart="@dimen/search_bar_margin"
- android:layout_marginEnd="@dimen/search_bar_margin"
- android:layout_marginBottom="@dimen/search_bar_margin_bottom">
+ android:layout_margin="@dimen/search_bar_margin">
<com.google.android.material.card.MaterialCardView
android:id="@+id/search_bar"
@@ -34,7 +31,7 @@
android:id="@+id/search_action_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/search_bar_height"
- android:layout_marginStart="-2dp"
+ android:paddingStart="4dp"
android:background="@drawable/search_bar_selected_background"
android:contentInsetStartWithNavigation="@dimen/search_bar_content_inset"
android:navigationIcon="@drawable/ic_homepage_search">
@@ -43,16 +40,11 @@
style="@style/TextAppearance.SearchBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:paddingStart="-4dp"
android:layout_gravity="start"
android:text="@string/search_menu"/>
</Toolbar>
</com.google.android.material.card.MaterialCardView>
- <ImageView
- android:id="@+id/account_avatar"
- android:layout_width="@dimen/avatar_length"
- android:layout_height="@dimen/avatar_length"
- android:layout_gravity="center_vertical"
- android:contentDescription="@string/search_bar_account_avatar_content_description"/>
</LinearLayout>
diff --git a/res/layout/settings_homepage_container.xml b/res/layout/settings_homepage_container.xml
index d9bcb83..4fd62fd 100644
--- a/res/layout/settings_homepage_container.xml
+++ b/res/layout/settings_homepage_container.xml
@@ -65,34 +65,26 @@
android:orientation="vertical"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed">
- <LinearLayout
- android:id="@+id/contextual_suggestion_content"
+ <ImageView
+ android:id="@+id/account_avatar"
+ android:layout_width="@dimen/avatar_length"
+ android:layout_height="@dimen/avatar_length"
+ android:layout_marginTop="@dimen/avatar_margin_top"
+ android:layout_marginEnd="@dimen/avatar_margin_end"
+ android:layout_gravity="end"
+ android:visibility="invisible"
+ android:accessibilityTraversalAfter="@id/homepage_title"
+ android:contentDescription="@string/search_bar_account_avatar_content_description"/>
+
+ <TextView
+ android:id="@+id/homepage_title"
+ android:text="@string/settings_label"
+ style="@style/HomepageTitleText"/>
+
+ <FrameLayout
+ android:id="@+id/suggestion_content"
android:layout_width="match_parent"
- android:layout_height="@dimen/suggestion_height"
- android:paddingHorizontal="@dimen/suggestion_padding_horizontal"
- android:paddingVertical="@dimen/suggestion_padding_vertical"
- android:orientation="vertical"
- android:gravity="bottom"
- android:visibility="gone">
-
- <TextView
- android:id="@+id/suggestion_title"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:text="@string/settings_label"
- style="@style/ContextualSuggestionText"/>
-
- <Button
- android:id="@+id/suggestion_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/suggestion_button_margin_top"
- android:paddingHorizontal="@dimen/suggestion_button_padding_horizontal"
- android:visibility="gone"
- style="@style/ActionPrimaryButton"/>
-
- </LinearLayout>
+ android:layout_height="wrap_content"/>
<include layout="@layout/search_bar"/>
diff --git a/res/layout/supported_links_dialog_item.xml b/res/layout/supported_links_dialog_item.xml
new file mode 100644
index 0000000..bbd2857
--- /dev/null
+++ b/res/layout/supported_links_dialog_item.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<CheckedTextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:ellipsize="marquee"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:textAlignment="viewStart"
+ android:textAppearance="?android:attr/textAppearanceListItemSmall"
+ android:textColor="?android:attr/textColorAlertDialogListItem"
+ android:drawableLeft="?android:attr/listChoiceIndicatorMultiple" />
+
diff --git a/res/layout/verified_links_widget.xml b/res/layout/verified_links_widget.xml
new file mode 100644
index 0000000..322ddd5
--- /dev/null
+++ b/res/layout/verified_links_widget.xml
@@ -0,0 +1,25 @@
+<?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:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <include layout="@layout/preference_widget_info"
+ android:id="@+id/verified_links_widget" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 73f669b..9e264f9 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -13,218 +13,254 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="0dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp" >
+ <TextView
android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <LinearLayout android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="24dp">
+ android:layout_height="wrap_content"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Small"
+ android:id="@+id/dialog_alert_subtitle"
+ android:text="@string/vpn_insecure_dialog_subtitle"
+ android:textColor="?android:attr/colorError"
+ android:visibility="gone"
+ android:includeFontPadding="false"
+ android:padding="0dp" />
- <LinearLayout android:id="@+id/editor"
- android:layout_width="match_parent"
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:visibility="gone">
+ android:paddingTop="24dp">
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_name"
- android:labelFor="@+id/name"/>
- <EditText style="@style/vpn_value" android:id="@+id/name"
- android:inputType="textCapWords"/>
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_type"
- android:labelFor="@+id/type"/>
- <Spinner style="@style/vpn_value"
- android:id="@+id/type"
- android:prompt="@string/vpn_type"
- android:entries="@array/vpn_types"/>
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_server"
- android:labelFor="@+id/server"/>
- <EditText style="@style/vpn_value" android:id="@+id/server"/>
-
- <CheckBox style="@style/vpn_value" android:id="@+id/mppe"
- android:text="@string/vpn_mppe"
- android:singleLine="false"
- android:visibility="gone"/>
-
- <LinearLayout android:id="@+id/l2tp"
+ <LinearLayout android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
+
<TextView style="@style/vpn_label"
- android:text="@string/vpn_l2tp_secret"
- android:labelFor="@+id/l2tp_secret"/>
- <EditText style="@style/vpn_value" android:id="@+id/l2tp_secret"
- android:password="true"
- android:hint="@string/vpn_not_used"/>
+ android:text="@string/vpn_name"
+ android:labelFor="@+id/name"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/name"
+ android:inputType="textCapWords"/>
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_type"
+ android:labelFor="@+id/type"/>
+ <Spinner style="@style/vpn_value"
+ android:id="@+id/type"
+ android:prompt="@string/vpn_type"
+ android:entries="@array/vpn_types"/>
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_server"
+ android:labelFor="@+id/server"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/server"/>
+
+ <CheckBox style="@style/vpn_value"
+ android:id="@+id/mppe"
+ android:text="@string/vpn_mppe"
+ android:singleLine="false"
+ android:visibility="gone"/>
+
+ <LinearLayout android:id="@+id/l2tp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_l2tp_secret"
+ android:labelFor="@+id/l2tp_secret"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/l2tp_secret"
+ android:password="true"
+ android:hint="@string/vpn_not_used"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/options_ipsec_identity"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_ipsec_identifier"
+ android:labelFor="@+id/ipsec_identifier"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/ipsec_identifier"
+ android:hint="@string/vpn_not_used"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_psk"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_ipsec_secret"
+ android:labelFor="@+id/ipsec_secret"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/ipsec_secret"
+ android:password="true"/>
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_user"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_ipsec_user_cert"
+ android:labelFor="@+id/ipsec_user_cert"/>
+ <Spinner style="@style/vpn_value"
+ android:id="@+id/ipsec_user_cert"
+ android:prompt="@string/vpn_ipsec_user_cert" />
+ </LinearLayout>
+
+ <LinearLayout android:id="@+id/ipsec_peer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_ipsec_ca_cert"
+ android:labelFor="@+id/ipsec_ca_cert"/>
+ <Spinner style="@style/vpn_value"
+ android:id="@+id/ipsec_ca_cert"
+ android:prompt="@string/vpn_ipsec_ca_cert" />
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_ipsec_server_cert"
+ android:labelFor="@+id/ipsec_server_cert"/>
+ <Spinner style="@style/vpn_value"
+ android:id="@+id/ipsec_server_cert"
+ android:prompt="@string/vpn_ipsec_server_cert" />
+ </LinearLayout>
+
+ <CheckBox style="@style/vpn_value"
+ android:id="@+id/show_options"
+ android:singleLine="false"
+ android:text="@string/vpn_show_options"/>
</LinearLayout>
- <LinearLayout android:id="@+id/options_ipsec_identity"
+ <LinearLayout android:id="@+id/options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone">
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_ipsec_identifier"
- android:labelFor="@+id/ipsec_identifier"/>
- <EditText style="@style/vpn_value" android:id="@+id/ipsec_identifier"
- android:hint="@string/vpn_not_used"/>
+ <LinearLayout android:id="@+id/network_options"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_search_domains"
+ android:labelFor="@+id/search_domains"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/search_domains"
+ android:hint="@string/vpn_not_used"/>
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_dns_servers"
+ android:labelFor="@+id/dns_servers"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/dns_servers"
+ android:hint="@string/vpn_not_used"/>
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_routes"
+ android:labelFor="@+id/routes"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/routes"
+ android:hint="@string/vpn_not_used"/>
+ </LinearLayout>
+
+ <TextView android:id="@+id/vpn_proxy_settings_title"
+ style="@style/vpn_label"
+ android:text="@string/proxy_settings_title"
+ android:labelFor="@+id/vpn_proxy_settings" />
+
+ <Spinner android:id="@+id/vpn_proxy_settings"
+ style="@style/vpn_value"
+ android:prompt="@string/proxy_settings_title"
+ android:entries="@array/vpn_proxy_settings" />
+
+ <LinearLayout
+ android:id="@+id/vpn_proxy_fields"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <TextView
+ style="@style/vpn_label"
+ android:text="@string/proxy_hostname_label"
+ android:labelFor="@+id/vpn_proxy_host" />
+
+ <EditText
+ android:id="@+id/vpn_proxy_host"
+ style="@style/vpn_value"
+ android:hint="@string/proxy_hostname_hint"
+ android:inputType="textNoSuggestions" />
+
+ <TextView
+ style="@style/vpn_label"
+ android:text="@string/proxy_port_label"
+ android:labelFor="@+id/vpn_proxy_port" />
+
+ <EditText
+ android:id="@+id/vpn_proxy_port"
+ style="@style/vpn_value"
+ android:hint="@string/proxy_port_hint"
+ android:inputType="number" />
+ </LinearLayout>
</LinearLayout>
- <LinearLayout android:id="@+id/ipsec_psk"
+ <LinearLayout android:id="@+id/userpass"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
+ android:orientation="vertical">
+
<TextView style="@style/vpn_label"
- android:text="@string/vpn_ipsec_secret"
- android:labelFor="@+id/ipsec_secret"/>
- <EditText style="@style/vpn_value" android:id="@+id/ipsec_secret"
+ android:text="@string/vpn_username"
+ android:labelFor="@+id/username"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/username"/>
+
+ <TextView style="@style/vpn_label"
+ android:text="@string/vpn_password"
+ android:labelFor="@+id/password"/>
+ <EditText style="@style/vpn_value"
+ android:id="@+id/password"
android:password="true"/>
+
+ <CheckBox style="@style/vpn_value"
+ android:id="@+id/save_login"
+ android:singleLine="false"
+ android:text="@string/vpn_save_login"/>
</LinearLayout>
- <LinearLayout android:id="@+id/ipsec_user"
+ <LinearLayout android:id="@+id/connect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:visibility="gone">
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_ipsec_user_cert"
- android:labelFor="@+id/ipsec_user_cert"/>
- <Spinner style="@style/vpn_value" android:id="@+id/ipsec_user_cert"
- android:prompt="@string/vpn_ipsec_user_cert" />
- </LinearLayout>
-
- <LinearLayout android:id="@+id/ipsec_peer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_ipsec_ca_cert"
- android:labelFor="@+id/ipsec_ca_cert"/>
- <Spinner style="@style/vpn_value" android:id="@+id/ipsec_ca_cert"
- android:prompt="@string/vpn_ipsec_ca_cert" />
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_ipsec_server_cert"
- android:labelFor="@+id/ipsec_server_cert"/>
- <Spinner style="@style/vpn_value" android:id="@+id/ipsec_server_cert"
- android:prompt="@string/vpn_ipsec_server_cert" />
- </LinearLayout>
-
- <CheckBox style="@style/vpn_value" android:id="@+id/show_options"
- android:singleLine="false"
- android:text="@string/vpn_show_options"/>
- </LinearLayout>
-
- <LinearLayout android:id="@+id/options"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
- <LinearLayout android:id="@+id/network_options"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_search_domains"
- android:labelFor="@+id/search_domains"/>
- <EditText style="@style/vpn_value" android:id="@+id/search_domains"
- android:hint="@string/vpn_not_used"/>
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_dns_servers"
- android:labelFor="@+id/dns_servers"/>
- <EditText style="@style/vpn_value" android:id="@+id/dns_servers"
- android:hint="@string/vpn_not_used"/>
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_routes"
- android:labelFor="@+id/routes"/>
- <EditText style="@style/vpn_value" android:id="@+id/routes"
- android:hint="@string/vpn_not_used"/>
- </LinearLayout>
-
- <TextView android:id="@+id/vpn_proxy_settings_title"
- style="@style/vpn_label"
- android:text="@string/proxy_settings_title"
- android:labelFor="@+id/vpn_proxy_settings" />
-
- <Spinner android:id="@+id/vpn_proxy_settings"
- style="@style/vpn_value"
- android:prompt="@string/proxy_settings_title"
- android:entries="@array/vpn_proxy_settings" />
-
- <LinearLayout
- android:id="@+id/vpn_proxy_fields"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone" >
-
- <TextView
- style="@style/vpn_label"
- android:text="@string/proxy_hostname_label"
- android:labelFor="@+id/vpn_proxy_host" />
-
- <EditText
- android:id="@+id/vpn_proxy_host"
- style="@style/vpn_value"
- android:hint="@string/proxy_hostname_hint"
- android:inputType="textNoSuggestions" />
-
- <TextView
- style="@style/vpn_label"
- android:text="@string/proxy_port_label"
- android:labelFor="@+id/vpn_proxy_port" />
-
- <EditText
- android:id="@+id/vpn_proxy_port"
- style="@style/vpn_value"
- android:hint="@string/proxy_port_hint"
- android:inputType="number" />
+ android:animateLayoutChanges="true">
+ <CheckBox style="@style/vpn_value"
+ android:id="@+id/always_on_vpn"
+ android:singleLine="false"
+ android:text="@string/vpn_menu_lockdown"/>
+ <TextView style="@style/vpn_warning"
+ android:id="@+id/always_on_invalid_reason"
+ android:singleLine="false"
+ android:visibility="gone"/>
</LinearLayout>
</LinearLayout>
-
- <LinearLayout android:id="@+id/userpass"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_username"
- android:labelFor="@+id/username"/>
- <EditText style="@style/vpn_value" android:id="@+id/username"/>
-
- <TextView style="@style/vpn_label"
- android:text="@string/vpn_password"
- android:labelFor="@+id/password"/>
- <EditText style="@style/vpn_value" android:id="@+id/password"
- android:password="true"/>
-
- <CheckBox style="@style/vpn_value" android:id="@+id/save_login"
- android:singleLine="false"
- android:text="@string/vpn_save_login"/>
- </LinearLayout>
-
- <LinearLayout android:id="@+id/connect"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:animateLayoutChanges="true">
- <CheckBox style="@style/vpn_value" android:id="@+id/always_on_vpn"
- android:singleLine="false"
- android:text="@string/vpn_menu_lockdown"/>
- <TextView style="@style/vpn_warning" android:id="@+id/always_on_invalid_reason"
- android:singleLine="false"
- android:visibility="gone"/>
- </LinearLayout>
- </LinearLayout>
-</ScrollView>
+ </ScrollView>
+</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-night/colors.xml b/res/values-night/colors.xml
index 4eb0afd..b2f9a89 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -21,7 +21,7 @@
<color name="homepage_support_background">#3F5FBD</color>
<color name="homepage_card_dismissal_background">@*android:color/material_grey_900</color>
<color name="contextual_card_background">@*android:color/material_grey_900</color>
- <color name="search_bar_background">@*android:color/material_grey_800</color>
+ <color name="search_bar_background">@*android:color/material_grey_900</color>
<!-- Dialog background color. -->
<color name="dialog_background">@*android:color/material_grey_800</color>
<color name="notification_importance_selection_bg">@*android:color/material_grey_800</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 984906f..5a8f636 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -509,4 +509,20 @@
<!-- Cell broacast receiver package name -->
<string name="config_cell_broadcast_receiver_package" translatable="false">com.android.cellbroadcastreceiver.module</string>
+
+ <!-- TODO(b/174964885): These media Uri are not defined in framework yet. Replace with framework defined variables once it's available. -->
+ <!-- Media Uri to view images storage category. -->
+ <string name="config_images_storage_category_uri" translatable="false">content://com.android.providers.media.documents/root/images_root</string>
+
+ <!-- Media Uri to view videos storage category. -->
+ <string name="config_videos_storage_category_uri" translatable="false">content://com.android.providers.media.documents/root/videos_root</string>
+
+ <!-- Media Uri to view audios storage category. -->
+ <string name="config_audios_storage_category_uri" translatable="false">content://com.android.providers.media.documents/root/audio_root</string>
+
+ <!-- Media Uri to view documents & other storage category. -->
+ <string name="config_documents_and_other_storage_category_uri" translatable="false">content://com.android.providers.media.documents/root/documents_root</string>
+
+ <!-- Whether to show Smart Storage toggle -->
+ <bool name="config_show_smart_storage_toggle">true</bool>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0ee39cd..e0e0219 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -131,25 +131,21 @@
<dimen name="switchbar_subsettings_margin_start">72dp</dimen>
<dimen name="switchbar_subsettings_margin_end">16dp</dimen>
- <!-- Search bar and avatar -->
- <dimen name="search_bar_margin">24dp</dimen>
- <dimen name="search_bar_margin_bottom">16dp</dimen>
- <dimen name="search_bar_height">48dp</dimen>
- <dimen name="search_bar_text_size">16sp</dimen>
- <dimen name="search_bar_card_elevation">2dp</dimen>
- <dimen name="search_bar_content_inset">64dp</dimen>
- <dimen name="avatar_length">@dimen/search_bar_height</dimen>
+ <!-- Search bar -->
+ <dimen name="search_bar_margin">16dp</dimen>
+ <dimen name="search_bar_height">52dp</dimen>
+ <dimen name="search_bar_text_size">20sp</dimen>
+ <dimen name="search_bar_corner_radius">28dp</dimen>
+ <dimen name="search_bar_content_inset">56dp</dimen>
- <!-- Contextual suggestions -->
- <dimen name="suggestion_height">232dp</dimen>
- <dimen name="suggestion_padding_horizontal">24dp</dimen>
- <dimen name="suggestion_padding_vertical">8dp</dimen>
- <dimen name="suggestion_button_margin_top">16dp</dimen>
- <dimen name="suggestion_button_padding_horizontal">24dp</dimen>
+ <!-- Avatar -->
+ <dimen name="avatar_length">48dp</dimen>
+ <dimen name="avatar_margin_top">56dp</dimen>
+ <dimen name="avatar_margin_end">24dp</dimen>
- <!-- Tool bar text -->
- <dimen name="tool_bar_max_text_size">36sp</dimen>
- <dimen name="tool_bar_min_text_size">24sp</dimen>
+ <!-- Homepage title -->
+ <dimen name="homepage_title_margin_bottom">8dp</dimen>
+ <dimen name="homepage_title_margin_horizontal">24dp</dimen>
<!-- Dimensions for Wifi Assistant Card -->
<dimen name="wifi_assistant_padding_top_bottom">16dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ae62962..e5784e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -684,6 +684,11 @@
</plurals>
<!-- Location settings, loading the number of apps which have location permission [CHAR LIMIT=30] -->
<string name="location_settings_loading_app_permission_stats">Loading\u2026</string>
+ <!-- Location settings footer warning text [CHAR LIMIT=NONE] -->
+ <string name="location_settings_footer">
+ Location may use sources like GPS, Wi\u2011Fi, mobile networks, and sensors to help estimate
+ your device\u2019s location.
+ </string>
<!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
<string name="account_settings_title">Accounts</string>
@@ -839,6 +844,8 @@
<string name="security_settings_face_settings_use_face_for_apps">App sign-in \u0026 payments</string>
<!-- Title for a category shown for the face settings page, followed by items that the user can toggle on/off to require/disable. -->
<string name="security_settings_face_settings_require_category">Requirements for face unlock</string>
+ <!-- Title for a category shown for the face settings page, followed by items that the user can toggle on/off to require/disable. [CHAR LIMIT=50] -->
+ <string name="security_settings_face_settings_preferences_category">When using face unlock</string>
<!-- Text shown on a toggle which disables/enables face unlock, depending if the user's eyes are open. [CHAR LIMIT=30] -->
<string name="security_settings_face_settings_require_attention">Require eyes to be open</string>
<!-- Text shown on the details of a toggle which disables/enables face unlock, depending if the user's eyes are open. [CHAR LIMIT=70] -->
@@ -849,6 +856,8 @@
<string name="security_settings_face_settings_require_confirmation_details">When using face unlock in apps, always require confirmation step</string>
<!-- Button text in face settings which removes the user's faces from the device [CHAR LIMIT=20] -->
<string name="security_settings_face_settings_remove_face_data">Delete face data</string>
+ <!-- Button text in face settings which removes the user's face model from the device [CHAR LIMIT=40] -->
+ <string name="security_settings_face_settings_remove_face_model">Delete face model</string>
<!-- Button text in face settings which lets the user enroll their face [CHAR LIMIT=40] -->
<string name="security_settings_face_settings_enroll">Set up face unlock</string>
<!-- Text shown in face settings explaining what your face can be used for. [CHAR LIMIT=NONE] -->
@@ -861,6 +870,10 @@
<string name="security_settings_face_settings_remove_dialog_title">Delete face data?</string>
<!-- Dialog contents shown when the user removes an enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_remove_dialog_details">The face data used by face unlock will be permanently and securely deleted. After removal, you will need your PIN, pattern, or password to unlock your phone, sign in to apps, and confirm payments.</string>
+ <!-- Dialog title shown when the user chooses to delete an existing enrolled face model. [CHAR LIMIT=35] -->
+ <string name="security_settings_face_settings_remove_model_dialog_title">Delete face model?</string>
+ <!-- Dialog contents shown when the user chooses to delete an existing enrolled face model. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_face_settings_remove_model_dialog_details">Your face model will be permanently and securely deleted.\n\nAfter deletion, you will need your fingerprint, PIN, pattern, or password to unlock your phone or for authentication in apps.</string>
<!-- Subtitle shown for contextual setting face enrollment [CHAR LIMIT=NONE] -->
<string name="security_settings_face_settings_context_subtitle">Use face unlock to unlock your phone</string>
@@ -1104,6 +1117,25 @@
<!-- Button to confirm the last removing the last fingerprint. [CHAR LIMIT=20]-->
<string name="fingerprint_last_delete_confirm">Yes, remove</string>
+ <!-- Title of the combined biometrics settings screen. [CHAR LIMIT=40] -->
+ <string name="security_settings_biometrics_title">Face \u0026 fingerprint unlock</string>
+ <!-- Summary of the current biometrics setting when no biometrics have been enrolled. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_summary_none">Tap to set up</string>
+ <!-- Summary of the current biometrics setting when the user has enrolled a face but no fingerprint. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_summary_face">Face only</string>
+ <!-- Summary of the current biometrics setting when the user has enrolled a fingerprint but no face. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_summary_fingerprint">Fingerprint only</string>
+ <!-- Summary of the current biometrics setting when the user has enrolled both a face and a fingerprint. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_summary_both">Face and fingerprint</string>
+ <!-- Description shown on the combined biometrics settings screen. [CHAR LIMIT=NONE] -->
+ <string name="security_settings_biometrics_description">When you set up face and fingerprint unlock, your phone will ask for your fingerprint when you wear a mask or are in a dark area</string>
+ <!-- Title of a section on the biometrics settings screen for setting up different types of authentication. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_types_category">Ways to unlock</string>
+ <!-- Title of a section on the biometrics settings screen for adjusting authentication preferences. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_preferences_category">Use face or fingerprint for</string>
+ <!-- Title of a section on the biometrics settings screen for adjusting authentication preferences. [CHAR LIMIT=50] -->
+ <string name="security_settings_biometrics_preference_use_with_apps">Authentication in apps</string>
+
<!-- Title of the preferences category for preference items to control encryption -->
<string name="crypt_keeper_settings_title">Encryption</string>
@@ -1368,15 +1400,15 @@
<string name="face_unlock_title">You can unlock your phone using your face. For security, this option requires a backup screen lock.</string>
<!-- Title for preference that guides the user through creating a backup unlock pattern for biometrics unlock [CHAR LIMIT=45]-->
- <string name="biometrics_unlock_set_unlock_pattern">Biometrics + Pattern</string>
+ <string name="biometrics_unlock_set_unlock_pattern">Pattern \u2022 Face \u2022 Fingerprint</string>
<!-- Title for preference that guides the user through creating a backup unlock PIN for biometrics unlock [CHAR LIMIT=45]-->
- <string name="biometrics_unlock_set_unlock_pin">Biometrics + PIN</string>
+ <string name="biometrics_unlock_set_unlock_pin">PIN \u2022 Face \u2022 Fingerprint</string>
<!-- Title for preference that guides the user through creating a backup unlock password for biometrics unlock [CHAR LIMIT=45]-->
- <string name="biometrics_unlock_set_unlock_password">Biometrics + Password</string>
+ <string name="biometrics_unlock_set_unlock_password">Password \u2022 Face \u2022 Fingerprint</string>
<!-- Title for preference that guides the user to skip face unlock setup [CHAR LIMIT=60]-->
- <string name="biometrics_unlock_skip_biometrics">Continue without biometrics</string>
+ <string name="biometrics_unlock_skip_biometrics">Continue without face or fingerprint</string>
<!-- Message shown in screen lock picker while setting up the new screen lock with biometrics option. [CHAR LIMIT=NONE] -->
- <string name="biometrics_unlock_title">You can unlock your phone using your biometrics. For security, this option requires a backup screen lock.</string>
+ <string name="biometrics_unlock_title">You can unlock your phone using your face or fingerprint. For security, this option requires a backup screen lock.</string>
<!-- Summary for preference that has been disabled by because of the DevicePolicyAdmin, or because device encryption is enabled, or because there are credentials in the credential storage [CHAR LIMIT=50] -->
<string name="unlock_set_unlock_disabled_summary">Disabled by admin, encryption policy, or
@@ -1736,7 +1768,7 @@
<!-- Bluetooth settings. Text displayed when Bluetooth is off and device list is empty [CHAR LIMIT=50]-->
<string name="bluetooth_empty_list_bluetooth_off">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.</string>
<!-- Bluetooth settings. Text displayed when Bluetooth is off and bluetooth scanning is turned on [CHAR LIMIT=NONE] -->
- <string name="bluetooth_scanning_on_info_message">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. you can change this in <annotation id="link">scanning settings</annotation>.</string>
+ <string name="bluetooth_scanning_on_info_message">When Bluetooth is turned on, your device can communicate with other nearby Bluetooth devices.\n\nTo improve device experience, apps and services can still scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services. you can change this in <annotation id="link">Bluetooth scanning settings</annotation>.</string>
<!-- Message to describe "BLE scan always available feature" when Bluetooth is off. The
place-holders "LINK_BEGIN" and "LINK_END" must NOT be translated. They mark a link to bring
the user to "scanning settings" screen. -->
@@ -1989,13 +2021,13 @@
<!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi
scanning is on. The place-holders "LINK_BEGIN" and "LINK_END" must NOT be translated. They
mark a link to bring the user to "scanning settings" screen. -->
- <string name="wifi_scan_notify_text">To improve location accuracy, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location-based features and services. You can change this in <xliff:g id="link_begin">LINK_BEGIN</xliff:g>scanning settings<xliff:g id="link_end">LINK_END</xliff:g>.</string>
+ <string name="wifi_scan_notify_text">To improve location accuracy, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. This can be used, for example, to improve location-based features and services. You can change this in <xliff:g id="link_begin">LINK_BEGIN</xliff:g>Wi\u2011Fi scanning settings<xliff:g id="link_end">LINK_END</xliff:g>.</string>
<!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi
scanning is also off. The place-holders "LINK_BEGIN" and "LINK_END" must NOT be translated.
They mark a link to bring the user to "scanning settings" screen. -->
<string name="wifi_scan_notify_text_scanning_off">To improve location accuracy,
turn on Wi-Fi scanning in
- <xliff:g id="link_begin">LINK_BEGIN</xliff:g>scanning
+ <xliff:g id="link_begin">LINK_BEGIN</xliff:g>Wi\u2011Fi scanning
settings<xliff:g id="link_end">LINK_END</xliff:g>.</string>
<!-- Wifi scan always mode checkbox text -->
<string name="wifi_scan_notify_remember_choice">Don\u2019t show again</string>
@@ -2354,6 +2386,8 @@
<string name="wifi_details_title">Network details</string>
<!-- Wifi details preference title to display router IP subnet mask -->
<string name="wifi_details_subnet_mask">Subnet mask</string>
+ <!-- Wifi details preference title to display wifi type info [CHAR LIMIT=50]-->
+ <string name="wifi_type_title">TYPE</string>
<!-- Wifi details preference title to display router DNS info -->
<string name="wifi_details_dns">DNS</string>
<!-- Wifi details preference category title for IPv6 information -->
@@ -2389,6 +2423,12 @@
<string name="wifi_gateway">Gateway</string>
<!-- Label for the network prefix of the network [CHAR LIMIT=25]-->
<string name="wifi_network_prefix_length">Network prefix length</string>
+ <!-- Summary for the WiFi Type WIFI_STANDARD_11AX [CHAR LIMIT=50]-->
+ <string name="wifi_type_11AX">Wi\u2011Fi 6</string>
+ <!-- Summary for the WiFi Type WIFI_STANDARD_11AC [CHAR LIMIT=50]-->
+ <string name="wifi_type_11AC">Wi\u2011Fi 5</string>
+ <!-- Summary for the WiFi Type WIFI_STANDARD_11N [CHAR LIMIT=50]-->
+ <string name="wifi_type_11N">Wi\u2011Fi 4</string>
<!-- Wi-Fi p2p / Wi-Fi Direct settings -->
<!-- Used in the 1st-level settings screen to launch Wi-fi Direct settings [CHAR LIMIT=25] -->
@@ -2999,6 +3039,8 @@
<string name="wallpaper_settings_title">Wallpaper</string>
<!-- Styles & Wallpapers settings title [CHAR LIMIT=30] -->
<string name="style_and_wallpaper_settings_title">Styles & wallpapers</string>
+ <!-- Summary for the top level Styles & wallpapers Settings [CHAR LIMIT=NONE]-->
+ <string name="wallpaper_dashboard_summary">Colors, app grid</string>
<!-- Wallpaper settings summary when default wallpaper is used [CHAR LIMIT=NONE] -->
<string name="wallpaper_settings_summary_default">Default</string>
<!-- Wallpaper settings summary when wallpaper has been updated [CHAR LIMIT=NONE] -->
@@ -3408,6 +3450,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>
@@ -3434,22 +3480,6 @@
<!-- Section header above list of external storage devices [CHAR LIMIT=30]-->
<string name="storage_external_title">Portable storage</string>
- <!-- Label for the settings activity for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
- <string name="alarms_and_reminders_label">Alarms and reminders</string>
- <!-- Label for the switch to toggler the permission for scheduling alarms [CHAR LIMIT=50] -->
- <string name="alarms_and_reminders_switch_title">Allow to set alarms or reminders</string>
- <!-- Title for the setting screen for controlling apps that can schedule alarms [CHAR LIMIT=30] -->
- <string name="alarms_and_reminders_title">Alarms and reminders</string>
- <!-- Description that appears below the alarms_and_reminders switch [CHAR LIMIT=NONE] -->
- <string name="alarms_and_reminders_footer_title">
- Allow this app to schedule alarms or other timing based events.
- This will allow the app to wake up and run even when you are not using the device.
- Note that revoking this permission may cause the app to malfunction, specifically any alarms
- that the app has scheduled will no longer work.
- </string>
- <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
- <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, event</string>
-
<!-- Summary of a single storage volume, constrasting available and total storage space. [CHAR LIMIT=48]-->
<string name="storage_volume_summary"><xliff:g id="used" example="1.2GB">%1$s</xliff:g> used of <xliff:g id="total" example="32GB">%2$s</xliff:g></string>
<!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
@@ -4115,6 +4145,18 @@
<!-- [CHAR LIMIT=60] Date&Time settings screen, toggle button title -->
<string name="location_time_zone_detection_toggle_title">Use location to set time zone</string>
+ <!-- [CHAR LIMIT=60] Date&Time settings screen, title of the dialog shown when user tries to
+ enable GeoTZ when Location toggle is off. -->
+ <string name="location_time_zone_detection_location_is_off_dialog_title">Device location needed</string>
+ <!-- [CHAR LIMIT=NONE] Date&Time settings screen, message on the dialog shown when user tries to
+ enable GeoTZ when Location toggle is off. -->
+ <string name="location_time_zone_detection_location_is_off_dialog_message">To set the time zone using your location, turn on location, then update time zone settings</string>
+ <!-- [CHAR LIMIT=30] Date&Time settings screen, button on a dialog shown when user tries to
+ enable GeoTZ, but Location toggle is off, which leads to Location settings page. -->
+ <string name="location_time_zone_detection_location_is_off_dialog_ok_button">Location settings</string>
+ <!-- [CHAR LIMIT=30] Date&Time settings screen, button on a dialog shown when user tries to
+ enable GeoTZ, but Location toggle is off, which closes the dialog. -->
+ <string name="location_time_zone_detection_location_is_off_dialog_cancel_button">Cancel</string>
<!-- [CHAR LIMIT=NONE] Location settings screen, summary when location time zone detection is not
applicable due to other settings like the "automatic time zone detection enabled" setting
being set to "off". -->
@@ -5101,7 +5143,7 @@
<!-- 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] -->
- <string name="accessibility_magnification_switch_shortcut_message">Using triple-tap to magnify part of the screen causes typing and other delays.\n\nThe accessibility button floats on your screen over other apps. Tap it to magnify.</string>
+ <string name="accessibility_magnification_switch_shortcut_message">Using triple-tap to magnify part of your screen causes typing and other delays.\n\nThe accessibility button floats on your screen over other apps. Tap it to magnify.</string>
<!-- Title for the switch shortcut button in accessibility switch shortcut dialog to change the config shortcut value. [CHAR LIMIT=45] -->
<string name="accessibility_magnification_switch_shortcut_positive_button">Switch to accessibility button</string>
<!-- Title for the cancel button in accessibility switch shortcut dialog to keep the config shortcut value. [CHAR LIMIT=54] -->
@@ -5841,6 +5883,8 @@
<string name="history_details_title">History details</string>
<!-- Preference title for advanced battery usage [CHAR LIMIT=40] -->
<string name="advanced_battery_preference_title">View battery usage</string>
+ <!-- Preference summary for advanced battery usage [CHAR LIMIT=40] -->
+ <string name="advanced_battery_preference_summary">View usage for past 24 hours</string>
<!-- Activity title for battery usage details for an app. or power consumer -->
<string name="battery_details_title">Battery usage</string>
@@ -5977,7 +6021,7 @@
<string name="battery_auto_restriction_summary">Detect when apps drain battery</string>
<!-- Summary for battery manager when it is on -->
- <string name="battery_manager_on" product="default">On / Detecting when apps drain battery</string>
+ <string name="battery_manager_summary">Detecting when apps drain battery</string>
<!-- Summary for battery manager when it is off -->
<string name="battery_manager_off">Off</string>
@@ -6243,6 +6287,9 @@
<!-- Battery saver: Label for preference to indicate there is a routine based schedule [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_routine">Based on your routine</string>
+ <!-- Battery saver: Summary for preference to indicate there is a routine based schedule [CHAR_LIMIT=40] -->
+ <string name="battery_saver_pref_auto_routine_summary">Will turn on based on your routine</string>
+
<!-- Battery saver: Label for preference to indicate there is a percentage based schedule [CHAR_LIMIT=40] -->
<string name="battery_saver_auto_percentage">Based on percentage</string>
@@ -6434,6 +6481,10 @@
<!-- Title of preference to enter the VPN settings activity -->
<string name="vpn_settings_title">VPN</string>
+ <!-- Title of preference to enter the VPN settings activity [CHAR LIMIT=30] -->
+ <string name="vpn_settings_insecure_single">Not secure</string>
+ <!-- Title of preference to enter the VPN settings activity [CHAR LIMIT=30] -->
+ <string name="vpn_settings_insecure_multiple"><xliff:g id="vpn_count" example="1">%d</xliff:g> not secure</string>
<!-- Title of Adaptive connectivity. Adaptive connectivity is a feature which automatically manages network connections for better battery life and performance. [CHAR LIMIT=60] -->
<string name="adaptive_connectivity_title">Adaptive connectivity</string>
@@ -6606,7 +6657,7 @@
<!-- Message displayed to let the user know that some of the options are disabled by admin. [CHAR LIMIT=NONE] -->
<string name="admin_disabled_other_options">Other options are disabled by your admin</string>
- <string name="admin_more_details">More details</string>
+ <string name="admin_more_details">Learn more</string>
<string name="notification_log_title">Notification log</string>
<string name="notification_history_title">Notification history</string>
@@ -7090,6 +7141,8 @@
<string name="vpn_always_on_summary_not_supported">Not supported by this app</string>
<!-- Preference summary for a VPN app that is set to be always-on. [CHAR LIMIT=40] -->
<string name="vpn_always_on_summary_active">Always-on active</string>
+ <!-- Preference summary for a VPN app that has an insecure type. [CHAR LIMIT=40] -->
+ <string name="vpn_insecure_summary">not secure VPN</string>
<!-- Preference title for the toggle that controls whether to force all network connections to
go through VPN. [CHAR LIMIT=40] -->
<string name="vpn_require_connection">Block connections without VPN</string>
@@ -7097,6 +7150,9 @@
[CHAR LIMIT=40] -->
<string name="vpn_require_connection_title">Require VPN connection?</string>
+ <!-- Dialog subtitle warning for a VPN app that has an insecure type. [CHAR LIMIT=100] -->
+ <string name="vpn_insecure_dialog_subtitle">This VPN is not secure. Update to an IKEv2 VPN</string>
+
<!-- Summary describing the always-on VPN feature. [CHAR LIMIT=NONE] -->
<string name="vpn_lockdown_summary">Select a VPN profile to always remain connected to. Network traffic will only be allowed when connected to this VPN.</string>
<!-- List item indicating that no always-on VPN is selected. [CHAR LIMIT=64] -->
@@ -7303,6 +7359,12 @@
<!-- Summary for the emergency info preference [CHAR LIMIT=40] -->
<string name="emergency_info_summary">Info & contacts for <xliff:g id="user_name" example="Jason">%1$s</xliff:g></string>
+ <!-- Button label for opening an arbitrary app [CHAR LIMIT=60] -->
+ <string name="open_app_button">Open <xliff:g id="app_name" example="Safety">%1$s</xliff:g></string>
+
+ <!-- Title for more settings button, clicking this button will open a new page containing more settings related to current page. [CHAR LIMIT=60] -->
+ <string name="more_settings_button">More Settings</string>
+
<!-- Application Restrictions screen title [CHAR LIMIT=45] -->
<string name="application_restrictions">Allow apps and content</string>
<!-- Applications with restrictions header [CHAR LIMIT=45] -->
@@ -7534,6 +7596,8 @@
<string name="help_uri_nfc_and_payment_settings" translatable="false"></string>
<!-- url for battery page if battery is not present -->
<string name="help_url_battery_missing" translatable="false"></string>
+ <!-- url for vpn page if connected vpn is not a secure type -->
+ <string name="help_url_insecure_vpn" translatable="false"></string>
<!-- User account title [CHAR LIMIT=30] -->
<string name="user_account_title">Account for content</string>
@@ -7800,10 +7864,10 @@
<string name="app_and_notification_dashboard_summary">Recent apps, default apps</string>
<!-- Toast shown when an app in the work profile attempts to open notification settings. The apps in the work profile cannot access notification settings. [CHAR LIMIT=NONE] -->
<string name="notification_settings_work_profile">Notification access is not available for apps in the work profile.</string>
- <!-- Title for setting tile leading to account settings [CHAR LIMIT=40]-->
- <string name="account_dashboard_title">Accounts</string>
- <!-- Summary for account settings tiles when there is no accounts on device [CHAR LIMIT=NONE]-->
- <string name="account_dashboard_default_summary">No accounts added</string>
+ <!-- Title for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=40]-->
+ <string name="account_dashboard_title">Passwords and accounts</string>
+ <!-- Summary for setting tile leading to saved autofill passwords, autofill, and account settings [CHAR LIMIT=NONE]-->
+ <string name="account_dashboard_default_summary">Saved passwords, autofill, synced accounts</string>
<!-- Title for setting tile leading to setting UI which allows user set default app to
handle actions such as open web page, making phone calls, default SMS apps [CHAR LIMIT=40]-->
<string name="app_default_dashboard_title">Default apps</string>
@@ -8423,6 +8487,9 @@
<!-- Configure Notifications Settings title. [CHAR LIMIT=30] -->
<string name="configure_notification_settings">Notifications</string>
+ <!-- Summary for the top level Notifications Settings [CHAR LIMIT=NONE]-->
+ <string name="notification_dashboard_summary">Notification history, conversations</string>
+
<!-- notification header - settings for conversation type notifications -->
<string name="conversation_notifs_category">Conversation</string>
@@ -9606,6 +9673,9 @@
settings button -->
<string name="notification_app_settings_button">Notification settings</string>
+ <!-- Content description for help icon button [CHAR LIMIT=20] -->
+ <string name="warning_button_text">Warning</string>
+
<!-- Generic label for suggestion card's ok button [CHAR LIMIT=20] -->
<string name="suggestion_button_text">Ok</string>
@@ -10555,6 +10625,9 @@
<!-- Summary for Display settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
<string name="display_dashboard_summary_with_style">Styles, wallpapers, screen timeout, font size</string>
+ <!-- Summary for the top level Display Settings [CHAR LIMIT=NONE]-->
+ <string name="display_dashboard_summary_2">Screen timeout, font size</string>
+
<!-- Summary for Display settings, explaining a few important settings under it [CHAR LIMIT=NONE]-->
<string name="display_dashboard_nowallpaper_summary">Sleep, font size</string>
@@ -11253,6 +11326,9 @@
<!-- Preference title for "Safety & emergency" settings page [CHAR LIMIT=60]-->
<string name="emergency_settings_preference_title">Safety & emergency</string>
+ <!-- Summary for the top level Safety & emergency Settings [CHAR LIMIT=NONE]-->
+ <string name="emergency_dashboard_summary">Emergency SOS, medical info, alerts</string>
+
<!-- Title text for edge to edge navigation [CHAR LIMIT=60] -->
<string name="edge_to_edge_navigation_title">Gesture navigation</string>
<!-- Summary text for edge to edge navigation [CHAR LIMIT=NONE] -->
@@ -11306,8 +11382,8 @@
<!-- Title text for swiping downwards on the bottom of the screen for notifications [CHAR LIMIT=60]-->
<string name="swipe_bottom_to_notifications_title">Swipe for notifications</string>
- <!-- Summary text for swiping downwards on the bottom of the screen for notifications [CHAR LIMIT=80]-->
- <string name="swipe_bottom_to_notifications_summary">Swipe down on the bottom edge of the screen to show your notifications</string>
+ <!-- Summary text for swiping downwards on the bottom of the screen for notifications [CHAR LIMIT=NONE]-->
+ <string name="swipe_bottom_to_notifications_summary">Swipe down on the bottom edge of the screen to show your notifications.\nYou can\'t use one-handed mode when this feature is turned on.</string>
<!-- Preference and settings suggestion title text for one handed [CHAR LIMIT=60] -->
<string name="one_handed_title">One-Handed mode</string>
@@ -11562,6 +11638,25 @@
<!-- Button label to allow the user to view additional information [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=2416766240581561009] -->
<string name="learn_more">Learn more</string>
+ <!-- Financed device Privacy --> <skip />
+
+ <!-- Title of setting on security settings screen on a financed device. This will take the user to a screen with information about what a device administrator can control and their impact on the user's privacy on a financed device. Shown on financed-managed devices only. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_settings">Financed device info</string>
+ <!-- Section header. This section shows what information a device administrator can see on a financed device. [CHAR LIMIT=60] -->
+ <string name="financed_privacy_exposure_category">Types of information your device administrator can see</string>
+ <!-- Label explaining that the device administrator can see data associated on the user's financed device. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_data">Data associated with your account, such as email and calendar info</string>
+ <!-- Section header. This section shows what changes a device administrator made to a financed device. [CHAR LIMIT=60] -->
+ <string name="financed_privacy_exposure_changes_category">Changes made by your device administrator</string>
+ <!-- Label explaining that the device admin can lock the device and change the user's password on their financed device. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_lock_device">Device administrator can lock this device and reset password</string>
+ <!-- Label explaining that the device admin can wipe the device remotely for a financed device. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_wipe_device">Device administrator can delete all device data</string>
+ <!-- Label explaining that the device admin configured the device to wipe itself when an incorrect password is entered too many times on a financed device. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_failed_password_wipe_device">Failed password attempts before deleting device data</string>
+ <!-- Financed Privacy settings activity header, summarizing the changes a credit provider can make to a financed device. [CHAR LIMIT=NONE] -->
+ <string name="financed_privacy_header">Your credit provider can change settings and install software on this device.\n\nTo learn more, contact your creditor provider.</string>
+
<!-- Strings for displaying which applications were set as default for specific actions. -->
<!-- Title for the apps that have been set as default handlers of camera-related intents. [CHAR LIMIT=30] -->
<plurals name="default_camera_app_title">
@@ -11604,6 +11699,39 @@
<!-- Preference label for the Files storage section. [CHAR LIMIT=50] -->
<string name="storage_files">Files</string>
+ <!-- Preference label for the Images storage section. [CHAR LIMIT=50] -->
+ <string name="storage_images">Images</string>
+
+ <!-- Preference label for the Videos storage section. [CHAR LIMIT=50] -->
+ <string name="storage_videos">Videos</string>
+
+ <!-- Preference label for the Audios storage section. [CHAR LIMIT=50] -->
+ <string name="storage_audios">Audios</string>
+
+ <!-- Preference label for the Apps storage section. [CHAR LIMIT=50] -->
+ <string name="storage_apps">Apps</string>
+
+ <!-- Preference label for the Documents & other storage section. [CHAR LIMIT=50] -->
+ <string name="storage_documents_and_other">Documents & other</string>
+
+ <!-- Preference label for the System storage section. [CHAR LIMIT=50] -->
+ <string name="storage_system">System</string>
+
+ <!-- Preference label for the Trash storage section. [CHAR LIMIT=50] -->
+ <string name="storage_trash">Trash</string>
+
+ <!-- Dialog title for the Trash storage section. [CHAR LIMIT=50] -->
+ <string name="storage_trash_dialog_title">Empty trash?</string>
+
+ <!-- Dialog message for the Trash storage section. [CHAR LIMIT=NONE] -->
+ <string name="storage_trash_dialog_ask_message">There are <xliff:g id="total" example="520MB">%1$s</xliff:g> of files in the trash. All items will be deleted forever, and you won\u2019t be able to restore them.</string>
+
+ <!-- Dialog message for the Trash storage section. [CHAR LIMIT=NONE] -->
+ <string name="storage_trash_dialog_empty_message">Trash is empty</string>
+
+ <!-- Dialog confirm button to empty all trash files. [CHAR LIMIT=50] -->
+ <string name="storage_trash_dialog_confirm">Empty trash</string>
+
<!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
<string name="storage_size_large_alternate"><xliff:g id="number" example="128">^1</xliff:g><small> <font size="20"><xliff:g id="unit" example="KB">^2</xliff:g></font></small></string>
<!-- Summary of a single storage volume total space. [CHAR LIMIT=48]-->
@@ -11611,6 +11739,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>
@@ -11639,6 +11771,12 @@
<string name="autofill_app">Autofill service</string>
<!-- Preference category for showing auto-fill services with saved passwords. [CHAR LIMIT=60] -->
<string name="autofill_passwords">Passwords</string>
+ <!-- Summary for passwords settings that shows how many passwords are saved for each autofill
+ service. [CHAR LIMIT=NONE] -->
+ <plurals name="autofill_passwords_count">
+ <item quantity="one"><xliff:g id="count">%1$d</xliff:g> password</item>
+ <item quantity="other"><xliff:g id="count">%1$d</xliff:g> passwords</item>
+ </plurals>
<!-- Keywords for the auto-fill feature. [CHAR LIMIT=NONE] -->
<string name="autofill_keywords">auto, fill, autofill, password</string>
@@ -12711,6 +12849,12 @@
<!-- Power menu setting privacy no secure screen lock set [CHAR_LIMIT=NONE] -->
<string name="power_menu_privacy_not_secure">To use, first set a screen lock</string>
+ <!-- Power menu setting use long press power to invoke assistant. [CHAR_LIMIT=NONE] -->
+ <string name="power_menu_long_press_for_assist">Hold for Assistant</string>
+
+ <!-- Power menu setting use log press power to invoke assistant summary. [CHAR_LIMIT=NONE] -->
+ <string name="power_menu_long_press_for_assist_summary">Trigger the Assistant by holding the power button</string>
+
<!-- Device controls toggle name [CHAR LIMIT=60] -->
<string name="device_controls_setting_toggle">Show device controls</string>
@@ -12820,10 +12964,6 @@
<string name="network_and_internet_preferences_summary">Connect to public networks</string>
<!-- Search keywords for "Internet" settings [CHAR_LIMIT=NONE] -->
<string name="keywords_internet">network connection, internet, wireless, data, wifi, wi-fi, wi fi, cellular, mobile, cell carrier, 4g, 3g, 2g, lte</string>
- <!-- Label text to view airplane-safe networks. [CHAR LIMIT=40] -->
- <string name="view_airplane_safe_networks">View airplane mode networks</string>
- <!-- Text of message for viewing the networks that are available in airplane mode. [CHAR LIMIT=60] -->
- <string name="viewing_airplane_mode_networks">Viewing airplane mode networks</string>
<!-- Slice title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
<string name="turn_on_wifi">Turn on Wi\u2011Fi</string>
<!-- Title for interrupting the voice call alert. [CHAR_LIMIT=NONE] -->
@@ -12836,6 +12976,8 @@
<string name="resetting_internet_text">Resetting your internet\u2026</string>
<!-- Menu option for data connectivity recovery for all requested technologies. [CHAR_LIMIT=NONE] -->
<string name="fix_connectivity">Fix connectivity</string>
+ <!-- Summary for networks available (includes no network connected). [CHAR_LIMIT=NONE] -->
+ <string name="networks_available">Networks available</string>
<!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
<string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
<!-- Panel subtitle for Wi-Fi turned on. [CHAR LIMIT=60] -->
@@ -12848,6 +12990,9 @@
Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_connection_active">Connected</string>
<!-- Provider Model:
+ Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+ <string name="mobile_data_no_connection">No connection</string>
+ <!-- Provider Model:
Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
<string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
<!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
@@ -12881,10 +13026,12 @@
<string name="category_name_brightness">Brightness</string>
<!-- Lock screen category name in Display Settings [CHAR LIMIT=none] -->
<string name="category_name_lock_display">Lock Display</string>
- <!-- Visibility category name in Display Settings [CHAR LIMIT=none] -->
- <string name="category_name_visibility">Visibility</string>
+ <!-- Appearance category name in Display Settings [CHAR LIMIT=none] -->
+ <string name="category_name_appearance">Appearance</string>
<!-- Color category name in Display Settings [CHAR LIMIT=none] -->
<string name="category_name_color">Color</string>
+ <!-- Name of Other display controls category in Display Settings [CHAR LIMIT=none] -->
+ <string name="category_name_display_controls">Other Display Controls</string>
<!-- Others category name [CHAR LIMIT=none] -->
<string name="category_name_others">Others</string>
<!-- General category name [CHAR LIMIT=none] -->
@@ -12962,4 +13109,13 @@
<!-- 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">Show clipboard access</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, images, or other content you\u2019ve copied</string>
+
+ <!-- All apps screen title, entry name on Apps page for the user to go to the all apps page. [CHAR LIMIT=30] -->
+ <string name="all_apps">All apps</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 0d773f1..8ca72db 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -310,6 +310,7 @@
<style name="TextAppearance.SearchBar"
parent="@*android:style/TextAppearance.DeviceDefault.Widget.Toolbar.Subtitle">
<item name="android:textSize">@dimen/search_bar_text_size</item>
+ <item name="android:textColor">?android:attr/textColorTertiary</item>
<item name="android:singleLine">true</item>
</style>
@@ -418,7 +419,6 @@
</style>
<style name="ActionPrimaryButton" parent="android:Widget.DeviceDefault.Button.Colored"/>
-
<style name="ActionSecondaryButton" parent="android:Widget.DeviceDefault.Button"/>
<style name="LockPatternContainerStyle">
@@ -481,11 +481,8 @@
</style>
<style name="SearchBarStyle">
- <item name="android:layout_marginEnd">@dimen/search_bar_margin</item>
- <item name="cardCornerRadius">34dp</item>
- <item name="enforceMaterialTheme">true</item>
- <item name="cardElevation">3dp</item>
- <item name="shapeAppearance">@null</item>
+ <item name="cardCornerRadius">@dimen/search_bar_corner_radius</item>
+ <item name="cardElevation">0dp</item>
</style>
<style name="ConditionCardBorderlessButton"
@@ -789,13 +786,11 @@
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
</style>
- <style name="ContextualSuggestionText"
- parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
- <item name="android:autoSizeTextType">uniform</item>
- <item name="android:autoSizeMaxTextSize">@dimen/tool_bar_max_text_size</item>
- <item name="android:autoSizeMinTextSize">@dimen/tool_bar_min_text_size</item>
- <item name="android:maxLines">3</item>
- <item name="android:gravity">bottom</item>
+ <style name="HomepageTitleText" parent="ToolbarText">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:layout_marginBottom">@dimen/homepage_title_margin_bottom</item>
+ <item name="android:layout_marginHorizontal">@dimen/homepage_title_margin_horizontal</item>
</style>
<style name="RequestManageCredentialsButtonPanel">
@@ -883,16 +878,7 @@
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
- <style name="ToolbarText.Collapsed"
- parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
- </style>
-
- <style name="ToolbarText"
- parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Title">
- <item name="android:textSize">@dimen/tool_bar_max_text_size</item>
- </style>
-
- <style name="ToolbarText.MoreThanTwoLines">
- <item name="android:textSize">@dimen/tool_bar_min_text_size</item>
- </style>
+ <style name="ToolbarText.Collapsed" parent="CollapsingToolbarTitle.Collapsed"/>
+ <style name="ToolbarText" parent="CollapsingToolbarTitle"/>
+ <style name="ToolbarText.MoreThanTwoLines" parent="CollapsingToolbarTitle.MoreThanTwoLines"/>
</resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index bed02c8..d7b3257 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -23,10 +23,7 @@
<item name="android:windowIsFloating">true</item>
</style>
- <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" />
-
<style name="Theme.Settings" parent="Theme.SettingsBase">
- <item name="preferenceTheme">@style/PreferenceTheme</item>
<item name="android:clipToPadding">false</item>
<item name="android:clipChildren">false</item>
<item name="android:listPreferredItemHeight">72dip</item>
diff --git a/res/xml/accounts_dashboard_settings.xml b/res/xml/accounts_dashboard_settings.xml
index d2affc7..c50eca4 100644
--- a/res/xml/accounts_dashboard_settings.xml
+++ b/res/xml/accounts_dashboard_settings.xml
@@ -22,27 +22,57 @@
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
- android:key="dashboard_tile_placeholder"
- android:order="30"/>
+ android:key="passwords_category"
+ android:order="10"
+ android:persistent="false"
+ android:title="@string/autofill_passwords"
+ settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
+ settings:keywords="@string/autofill_keywords" />
- <SwitchPreference
- android:key="auto_sync_account_data"
- android:title="@string/auto_sync_account_title"
- android:summary="@string/auto_sync_account_summary"
- android:order="102"
- settings:allowDividerAbove="true"/>
+ <PreferenceCategory
+ android:key="default_service_category"
+ android:order="20"
+ android:title="@string/autofill_app">
- <SwitchPreference
- android:key="auto_sync_work_account_data"
- android:title="@string/account_settings_menu_auto_sync_work"
- android:summary="@string/auto_sync_account_summary"
- settings:forWork="true"
- android:order="103"/>
+ <com.android.settings.widget.GearPreference
+ android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
+ android:key="default_autofill_main"
+ android:title="@string/autofill_app"
+ settings:keywords="@string/autofill_keywords">
+ <extra
+ android:name="for_work"
+ android:value="false" />
+ </com.android.settings.widget.GearPreference>
+ </PreferenceCategory>
- <SwitchPreference
- android:key="auto_sync_personal_account_data"
- android:title="@string/account_settings_menu_auto_sync_personal"
- android:summary="@string/auto_sync_account_summary"
- android:order="104"/>
+ <PreferenceCategory
+ android:key="accounts_category"
+ android:order="100"
+ android:title="@string/account_settings">
+
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"
+ android:order="130"/>
+
+ <SwitchPreference
+ android:key="auto_sync_account_data"
+ android:title="@string/auto_sync_account_title"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="202"
+ settings:allowDividerAbove="true"/>
+
+ <SwitchPreference
+ android:key="auto_sync_work_account_data"
+ android:title="@string/account_settings_menu_auto_sync_work"
+ android:summary="@string/auto_sync_account_summary"
+ settings:forWork="true"
+ android:order="203"/>
+
+ <SwitchPreference
+ android:key="auto_sync_personal_account_data"
+ android:title="@string/account_settings_menu_auto_sync_personal"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="204"/>
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/res/xml/accounts_personal_dashboard_settings.xml b/res/xml/accounts_personal_dashboard_settings.xml
index db57e3d..3d88cf9 100644
--- a/res/xml/accounts_personal_dashboard_settings.xml
+++ b/res/xml/accounts_personal_dashboard_settings.xml
@@ -23,20 +23,50 @@
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
- android:key="dashboard_tile_placeholder"
- android:order="30"/>
+ android:key="passwords_category"
+ android:order="10"
+ android:persistent="false"
+ android:title="@string/autofill_passwords"
+ settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
+ settings:keywords="@string/autofill_keywords" />
- <SwitchPreference
- android:key="auto_sync_account_data"
- android:title="@string/auto_sync_account_title"
- android:summary="@string/auto_sync_account_summary"
+ <PreferenceCategory
+ android:key="default_service_category"
+ android:order="20"
+ android:title="@string/autofill_app">
+
+ <com.android.settings.widget.GearPreference
+ android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
+ android:key="default_autofill_main"
+ android:title="@string/autofill_app"
+ settings:keywords="@string/autofill_keywords">
+ <extra
+ android:name="for_work"
+ android:value="false" />
+ </com.android.settings.widget.GearPreference>
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="accounts_category"
android:order="100"
- settings:allowDividerAbove="true"/>
+ android:title="@string/account_settings">
- <SwitchPreference
- android:key="auto_sync_personal_account_data"
- android:title="@string/account_settings_menu_auto_sync_personal"
- android:summary="@string/auto_sync_account_summary"
- android:order="110"/>
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"
+ android:order="130"/>
+
+ <SwitchPreference
+ android:key="auto_sync_account_data"
+ android:title="@string/auto_sync_account_title"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="200"
+ settings:allowDividerAbove="true"/>
+
+ <SwitchPreference
+ android:key="auto_sync_personal_account_data"
+ android:title="@string/account_settings_menu_auto_sync_personal"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="210"/>
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/res/xml/accounts_work_dashboard_settings.xml b/res/xml/accounts_work_dashboard_settings.xml
index b27357d..29e71e2 100644
--- a/res/xml/accounts_work_dashboard_settings.xml
+++ b/res/xml/accounts_work_dashboard_settings.xml
@@ -23,20 +23,50 @@
settings:keywords="@string/keywords_accounts">
<PreferenceCategory
- android:key="dashboard_tile_placeholder"
- android:order="30"/>
+ android:key="passwords_category"
+ android:order="10"
+ android:persistent="false"
+ android:title="@string/autofill_passwords"
+ settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController"
+ settings:keywords="@string/autofill_keywords" />
- <SwitchPreference
- android:key="auto_sync_account_data"
- android:title="@string/auto_sync_account_title"
- android:summary="@string/auto_sync_account_summary"
+ <com.android.settings.widget.WorkOnlyCategory
+ android:key="autofill_work_app_defaults"
+ android:order="30"
+ android:title="@string/default_for_work">
+
+ <com.android.settings.widget.GearPreference
+ android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
+ android:key="default_autofill_work"
+ android:title="@string/autofill_app"
+ settings:searchable="false">
+ <extra
+ android:name="for_work"
+ android:value="true" />
+ </com.android.settings.widget.GearPreference>
+ </com.android.settings.widget.WorkOnlyCategory>
+
+ <PreferenceCategory
+ android:key="accounts_category"
android:order="100"
- settings:allowDividerAbove="true"/>
+ android:title="@string/account_settings">
- <SwitchPreference
- android:key="auto_sync_work_account_data"
- android:title="@string/account_settings_menu_auto_sync_work"
- android:summary="@string/auto_sync_account_summary"
- android:order="110"/>
+ <PreferenceCategory
+ android:key="dashboard_tile_placeholder"
+ android:order="130"/>
+
+ <SwitchPreference
+ android:key="auto_sync_account_data"
+ android:title="@string/auto_sync_account_title"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="200"
+ settings:allowDividerAbove="true"/>
+
+ <SwitchPreference
+ android:key="auto_sync_work_account_data"
+ android:title="@string/account_settings_menu_auto_sync_work"
+ android:summary="@string/auto_sync_account_summary"
+ android:order="210"/>
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/res/xml/apps.xml b/res/xml/apps.xml
index cefb67a..68b5c9a 100644
--- a/res/xml/apps.xml
+++ b/res/xml/apps.xml
@@ -23,7 +23,7 @@
<Preference
android:key="all_app_infos"
- android:title="@string/applications_settings"
+ android:title="@string/all_apps"
android:summary="@string/summary_placeholder"
android:order="-999"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
diff --git a/res/xml/default_autofill_picker_settings.xml b/res/xml/default_autofill_picker_settings.xml
deleted file mode 100644
index 392f733..0000000
--- a/res/xml/default_autofill_picker_settings.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2018 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:key="default_autofill_picker"
- android:title="@string/autofill_app"
- settings:keywords="@string/autofill_keywords">
-
- <PreferenceCategory
- android:key="passwords_category"
- android:persistent="false"
- android:title="@string/autofill_passwords"
- settings:controller="com.android.settings.applications.autofill.PasswordsPreferenceController" >
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="default_service_category"
- android:title="@string/autofill_app">
-
- <!-- TODO(b/169455298): Fix the redundant title. -->
- <com.android.settings.widget.GearPreference
- android:key="default_autofill_main"
- android:title="@string/autofill_app"
- android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
- settings:searchable="false"
- settings:keywords="@string/autofill_keywords">
- <extra android:name="for_work" android:value="false" />
- </com.android.settings.widget.GearPreference>
- </PreferenceCategory>
-
-
- <com.android.settings.widget.WorkOnlyCategory
- android:key="autofill_work_app_defaults"
- android:title="@string/default_for_work">
-
- <com.android.settings.widget.GearPreference
- android:key="default_autofill_work"
- android:title="@string/autofill_app"
- android:fragment="com.android.settings.applications.defaultapps.DefaultAutofillPicker"
- settings:searchable="false"
- settings:keywords="@string/autofill_keywords">
- <extra android:name="for_work" android:value="true" />
- </com.android.settings.widget.GearPreference>
- </com.android.settings.widget.WorkOnlyCategory>
-</PreferenceScreen>
diff --git a/res/xml/display_settings_v2.xml b/res/xml/display_settings_v2.xml
index 0b43914..50879da 100644
--- a/res/xml/display_settings_v2.xml
+++ b/res/xml/display_settings_v2.xml
@@ -59,7 +59,7 @@
</PreferenceCategory>
<PreferenceCategory
- android:title="@string/category_name_visibility">
+ android:title="@string/category_name_appearance">
<com.android.settings.display.darkmode.DarkModePreference
android:key="dark_ui_mode"
@@ -102,7 +102,7 @@
</PreferenceCategory>
<PreferenceCategory
- android:title="@string/category_name_general">
+ android:title="@string/category_name_display_controls">
<SwitchPreference
android:key="auto_rotate"
diff --git a/res/xml/emergency_settings.xml b/res/xml/emergency_settings.xml
index 28ae8b1..80d6ac0 100644
--- a/res/xml/emergency_settings.xml
+++ b/res/xml/emergency_settings.xml
@@ -32,6 +32,13 @@
android:order="100"
android:fragment="com.android.settings.emergency.EmergencyGestureSettings"
settings:controller="com.android.settings.emergency.EmergencyGestureEntrypointPreferenceController" />
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="more_settings"
+ android:layout="@layout/more_settings_button"
+ android:order="180"
+ android:selectable="false"
+ settings:allowDividerBelow="true"
+ settings:controller="com.android.settings.emergency.MoreSettingsPreferenceController" />
<com.android.settingslib.RestrictedPreference
android:key="app_and_notif_cell_broadcast_settings"
android:title="@string/cell_broadcast_settings"
diff --git a/res/xml/financed_privacy_settings.xml b/res/xml/financed_privacy_settings.xml
new file mode 100644
index 0000000..742d7e1
--- /dev/null
+++ b/res/xml/financed_privacy_settings.xml
@@ -0,0 +1,84 @@
+<?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/financed_privacy_settings">
+
+ <PreferenceCategory android:key="exposure_category"
+ android:order="200"
+ android:title="@string/financed_privacy_exposure_category"
+ android:contentDescription="@string/financed_privacy_exposure_category">
+ <Preference android:key="enterprise_privacy_enterprise_data"
+ android:order="210"
+ android:layout_height="wrap_content"
+ android:title="@string/financed_privacy_data"
+ android:selectable="false"/>
+ <Preference android:key="enterprise_privacy_installed_packages"
+ android:order="220"
+ android:title="@string/enterprise_privacy_installed_packages"
+ android:selectable="false"/>
+ <Preference android:key="enterprise_privacy_usage_stats"
+ android:order="230"
+ android:title="@string/enterprise_privacy_usage_stats"
+ android:selectable="false"/>
+ <Preference android:key="network_logs"
+ android:order="240"
+ android:title="@string/enterprise_privacy_network_logs"
+ android:selectable="false"/>
+ <Preference android:key="bug_reports"
+ android:order="250"
+ android:title="@string/enterprise_privacy_bug_reports"
+ android:selectable="false"/>
+ <Preference android:key="security_logs"
+ android:order="260"
+ android:title="@string/enterprise_privacy_security_logs"
+ android:selectable="false"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/financed_privacy_exposure_changes_category"
+ android:order="300"
+ android:key="exposure_changes_category">
+ <Preference android:fragment="com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages"
+ android:order="310"
+ android:key="number_enterprise_installed_packages"
+ android:title="@string/enterprise_privacy_enterprise_installed_packages"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:key="device_access_category"
+ android:order="500"
+ android:title="@string/enterprise_privacy_device_access_category">
+ <Preference android:key="enterprise_privacy_lock_device"
+ android:order="510"
+ android:title="@string/financed_privacy_lock_device"
+ android:selectable="false"/>
+ <Preference android:key="enterprise_privacy_wipe_device"
+ android:order="520"
+ android:title="@string/financed_privacy_wipe_device"
+ android:selectable="false"/>
+ <Preference android:key="failed_password_wipe_current_user"
+ android:order="530"
+ android:title="@string/financed_privacy_failed_password_wipe_device"
+ android:selectable="false"/>
+ </PreferenceCategory>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="financed_privacy_footer"
+ android:title="@string/financed_privacy_header"
+ android:selectable="false"
+ settings:searchable="false"/>
+</PreferenceScreen>
diff --git a/res/xml/installed_app_launch_settings.xml b/res/xml/installed_app_launch_settings.xml
index b777949..d631e56 100644
--- a/res/xml/installed_app_launch_settings.xml
+++ b/res/xml/installed_app_launch_settings.xml
@@ -16,31 +16,59 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/launch_by_default">
- <PreferenceCategory android:key="app_launch_domain_links"
- android:title="@string/app_launch_domain_links_title">
+ <com.android.settingslib.widget.MainSwitchPreference
+ android:key="open_by_default_supported_links"
+ android:title="@string/app_launch_open_domain_urls_title"/>
- <Preference
- android:key="app_link_state"
- android:summary="@string/summary_placeholder"
- android:title="@string/app_launch_open_domain_urls_title"/>
+ <com.android.settingslib.widget.TopIntroPreference
+ android:key="open_by_default_top_intro"
+ android:title="@string/app_launch_top_intro_message"/>
- <com.android.settings.applications.AppDomainsPreference
- android:key="app_launch_supported_domain_urls"
- android:title="@string/app_launch_supported_domain_urls_title"
- android:dependency="app_link_state"
- />
+ <PreferenceCategory
+ android:layout="@layout/preference_category_no_label"
+ android:key="open_by_default_main_category"
+ settings:searchable="false">
+
+ <PreferenceCategory
+ android:title="@string/app_launch_links_category">
+
+ <com.android.settings.applications.intentpicker.VerifiedLinksPreference
+ android:key="open_by_default_verified_links"
+ android:title="@string/summary_placeholder"
+ android:order="-100"
+ settings:searchable="false"/>
+
+ <PreferenceCategory
+ android:layout="@layout/preference_category_no_label"
+ android:key="open_by_default_selected_links_category"
+ android:order="100"
+ settings:searchable="false"/>
+
+ <Preference
+ android:key="open_by_default_add_link"
+ android:title="@string/app_launch_add_link"
+ android:order="300"
+ android:icon="@drawable/ic_add_24dp"/>
+
+ </PreferenceCategory>
+
+ <PreferenceCategory android:key="app_launch_other_defaults"
+ android:title="@string/app_launch_other_defaults_title">
+
+ <com.android.settings.applications.ClearDefaultsPreference
+ android:key="app_launch_clear_defaults"
+ android:selectable="false"/>
+
+ </PreferenceCategory>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="open_by_default_footer"
+ android:selectable="false"
+ settings:allowDividerAbove="true"
+ settings:searchable="false"/>
</PreferenceCategory>
-
- <PreferenceCategory android:key="app_launch_other_defaults"
- android:title="@string/app_launch_other_defaults_title">
-
- <com.android.settings.applications.ClearDefaultsPreference
- android:key="app_launch_clear_defaults"
- android:selectable="false"/>
-
- </PreferenceCategory>
-
</PreferenceScreen>
diff --git a/res/xml/language_and_input.xml b/res/xml/language_and_input.xml
index 50968d9..d7b8364 100644
--- a/res/xml/language_and_input.xml
+++ b/res/xml/language_and_input.xml
@@ -54,12 +54,6 @@
android:persistent="false"
android:fragment="com.android.settings.inputmethod.SpellCheckersSettings" />
- <Preference
- android:key="default_autofill"
- android:title="@string/autofill_app"
- android:fragment="com.android.settings.applications.defaultapps.AutofillPicker"
- settings:keywords="@string/autofill_keywords" />
-
<!-- User dictionary preference title and fragment will be set programmatically. -->
<Preference
android:key="key_user_dictionary_settings"
diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml
index 93f30e5..df17052 100644
--- a/res/xml/location_settings.xml
+++ b/res/xml/location_settings.xml
@@ -38,7 +38,17 @@
<PreferenceCategory
android:key="location_advanced_settings"
android:layout="@layout/preference_category_no_label"
- settings:initialExpandedChildrenCount="2">
+ settings:initialExpandedChildrenCount="3">
+
+ <!-- This preference gets removed if there is no managed profile -->
+ <com.android.settingslib.RestrictedSwitchPreference
+ android:enabled="false"
+ android:key="managed_profile_location_switch"
+ android:selectable="true"
+ android:title="@string/managed_profile_location_switch_title"
+ settings:controller="com.android.settings.location.LocationForWorkPreferenceController"
+ settings:forWork="true"
+ settings:useAdminDisabledSummary="true"/>
<!-- This preference category gets removed if new_recent_location_ui is disabled -->
<Preference
@@ -57,28 +67,10 @@
android:title="@string/location_services_screen_title"
settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
- <!-- This preference gets removed if there is no managed profile -->
- <com.android.settingslib.RestrictedSwitchPreference
- android:enabled="false"
- android:key="managed_profile_location_switch"
- android:selectable="true"
- android:title="@string/managed_profile_location_switch_title"
- settings:controller="com.android.settings.location.LocationForWorkPreferenceController"
- settings:forWork="true"
- settings:useAdminDisabledSummary="true"/>
-
- <!-- This preference gets removed if there is no managed profile -->
- <PreferenceCategory
- android:key="location_services_managed_profile"
- android:title="@string/managed_profile_location_services"
- settings:forWork="true"
- settings:controller="com.android.settings.location.LocationInjectedServicesForWorkPreferenceController"/>
-
</PreferenceCategory>
- <PreferenceCategory
+ <com.android.settingslib.widget.FooterPreference
+ android:title="@string/location_settings_footer"
android:key="location_footer"
- android:layout="@layout/preference_category_no_label"
- settings:allowDividerAbove="false"
- settings:controller="com.android.settings.location.LocationFooterPreferenceController"/>
+ android:selectable="false"/>
</PreferenceScreen>
diff --git a/res/xml/location_settings_personal.xml b/res/xml/location_settings_personal.xml
index 0e971d6..0307a85 100644
--- a/res/xml/location_settings_personal.xml
+++ b/res/xml/location_settings_personal.xml
@@ -53,9 +53,8 @@
android:fragment="com.android.settings.location.LocationServices"
settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
- <PreferenceCategory
+ <com.android.settingslib.widget.FooterPreference
+ android:title="@string/location_settings_footer"
android:key="location_footer"
- android:layout="@layout/preference_category_no_label"
- settings:allowDividerAbove="false"
- settings:controller="com.android.settings.location.LocationFooterPreferenceController"/>
+ android:selectable="false"/>
</PreferenceScreen>
diff --git a/res/xml/location_settings_workprofile.xml b/res/xml/location_settings_workprofile.xml
index c3efcbe..5ec3e1b 100644
--- a/res/xml/location_settings_workprofile.xml
+++ b/res/xml/location_settings_workprofile.xml
@@ -61,9 +61,8 @@
android:fragment="com.android.settings.location.LocationServicesForWork"
settings:controller="com.android.settings.location.LocationServicesForWorkPreferenceController"/>
- <PreferenceCategory
+ <com.android.settingslib.widget.FooterPreference
+ android:title="@string/location_settings_footer"
android:key="location_footer"
- android:layout="@layout/preference_category_no_label"
- settings:allowDividerAbove="false"
- settings:controller="com.android.settings.location.LocationFooterPreferenceController"/>
+ android:selectable="false"/>
</PreferenceScreen>
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index bb7117d..4c760aa 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -98,7 +98,7 @@
android:order="10"
android:fragment="com.android.settings.datausage.DataSaverSummary"/>
- <com.android.settingslib.RestrictedPreference
+ <com.android.settings.vpn2.VpnInfoPreference
android:fragment="com.android.settings.vpn2.VpnSettings"
android:key="vpn_settings"
android:title="@string/vpn_settings_title"
diff --git a/res/xml/power_menu_settings.xml b/res/xml/power_menu_settings.xml
index c7fba71..bfe9c20 100644
--- a/res/xml/power_menu_settings.xml
+++ b/res/xml/power_menu_settings.xml
@@ -20,6 +20,13 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/power_menu_setting_name">
+ <SwitchPreference
+ android:key="gesture_power_menu_long_press_for_assist"
+ android:title="@string/power_menu_long_press_for_assist"
+ android:summary="@string/power_menu_long_press_for_assist_summary"
+ settings:controller="com.android.settings.gestures.LongPressPowerButtonPreferenceController"
+ />
+
<Preference
android:key="gesture_global_actions_panel_summary"
android:title="@string/cards_passes_sentence"
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/smart_battery_detail.xml b/res/xml/smart_battery_detail.xml
index 2d36119..c2b3d1f 100644
--- a/res/xml/smart_battery_detail.xml
+++ b/res/xml/smart_battery_detail.xml
@@ -22,11 +22,6 @@
android:title="@string/smart_battery_manager_title"
settings:searchable="false">
- <com.android.settingslib.widget.MainSwitchPreference
- android:key="smart_battery"
- android:title="@string/adaptive_battery_main_switch_title"
- settings:controller="com.android.settings.fuelgauge.SmartBatteryPreferenceController"/>
-
<com.android.settings.widget.VideoPreference
android:key="auto_awesome_battery"
android:title="@string/summary_placeholder"
@@ -35,6 +30,13 @@
settings:controller="com.android.settings.widget.VideoPreferenceController"/>
<SwitchPreference
+ android:key="smart_battery"
+ android:title="@string/smart_battery_title"
+ android:summary="@string/smart_battery_summary"
+ settings:controller="com.android.settings.fuelgauge.SmartBatteryPreferenceController"
+ settings:allowDividerAbove="true"/>
+
+ <SwitchPreference
android:key="auto_restriction"
android:title="@string/battery_auto_restriction_title"
android:summary="@string/battery_auto_restriction_summary"
diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml
index bc58d7e..9bca65a 100644
--- a/res/xml/storage_dashboard_fragment.xml
+++ b/res/xml/storage_dashboard_fragment.xml
@@ -19,59 +19,81 @@
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="1"
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="2"
+ settings:searchable="false"
+ settings:controller="com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController"
+ settings:allowDividerBelow="true"/>
<com.android.settings.widget.PrimarySwitchPreference
android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
android:key="toggle_asm"
android:title="@string/automatic_storage_manager_preference_title"
android:icon="@drawable/ic_storage"
- android:order="1"
+ android:order="3"
settings:allowDividerAbove="true"
- settings:allowDividerBelow="true"
- settings:controller="com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController" />
+ settings:controller="com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController"/>
+ <Preference
+ android:key="free_up_space"
+ android:order="4"
+ android:title="@string/storage_free_up_space_title"
+ android:summary="@string/storage_free_up_space_summary"
+ settings:allowDividerAbove="true"
+ android:icon="@drawable/ic_files_go_round"/>
+ <!-- Preference order 100~200 are 'ONLY' for storage category preferences below. -->
+ <Preference
+ android:key="pref_public_storage"
+ android:title="@string/storage_files"
+ android:icon="@drawable/ic_folder_vd_theme_24"
+ android:order="100"/>
<com.android.settings.deviceinfo.StorageItemPreference
- android:key="pref_photos_videos"
- android:title="@string/storage_photos_videos"
+ android:key="pref_images"
+ android:title="@string/storage_images"
android:icon="@drawable/ic_photo_library"
- settings:allowDividerAbove="true"
- android:order="2" />
+ android:order="101"/>
<com.android.settings.deviceinfo.StorageItemPreference
- android:key="pref_music_audio"
- android:title="@string/storage_music_audio"
+ android:key="pref_videos"
+ android:title="@string/storage_videos"
+ android:icon="@drawable/ic_local_movies"
+ android:order="102"/>
+ <com.android.settings.deviceinfo.StorageItemPreference
+ android:key="pref_audios"
+ android:title="@string/storage_audios"
android:icon="@drawable/ic_media_stream"
- android:order="3" />
+ android:order="103"/>
+ <com.android.settings.deviceinfo.StorageItemPreference
+ android:key="pref_apps"
+ android:title="@string/storage_apps"
+ android:icon="@drawable/ic_storage_apps"
+ android:order="104"/>
<com.android.settings.deviceinfo.StorageItemPreference
android:key="pref_games"
android:title="@string/storage_games"
android:icon="@drawable/ic_videogame_vd_theme_24"
- android:order="4" />
+ android:order="105"/>
<com.android.settings.deviceinfo.StorageItemPreference
- android:key="pref_movies"
- android:title="@string/storage_movies_tv"
- android:icon="@drawable/ic_local_movies"
- android:order="5" />
- <com.android.settings.deviceinfo.StorageItemPreference
- android:key="pref_other_apps"
- android:title="@string/storage_other_apps"
- android:icon="@drawable/ic_storage_apps"
- android:order="6" />
- <com.android.settings.deviceinfo.StorageItemPreference
- android:key="pref_files"
- android:title="@string/storage_files"
+ android:key="pref_documents_and_other"
+ android:title="@string/storage_documents_and_other"
android:icon="@drawable/ic_folder_vd_theme_24"
- android:order="7"
- settings:keywords="@string/keywords_storage_files"/>
+ android:order="106"/>
<com.android.settings.deviceinfo.StorageItemPreference
android:key="pref_system"
- android:title="@string/storage_detail_system"
+ android:title="@string/storage_system"
android:icon="@drawable/ic_system_update"
- android:order="100" />
+ android:order="107"/>
+ <com.android.settings.deviceinfo.StorageItemPreference
+ android:key="pref_trash"
+ android:title="@string/storage_trash"
+ android:icon="@drawable/ic_trash_can"
+ android:order="108"/>
+ <!-- Preference order 100~200 are 'ONLY' for storage category preferences above. -->
<PreferenceCategory
android:key="pref_secondary_users"
android:title="@string/storage_other_users"
- android:order="200" />
-</PreferenceScreen>
\ No newline at end of file
+ android:order="201" />
+</PreferenceScreen>
diff --git a/res/xml/swipe_bottom_to_notification_settings.xml b/res/xml/swipe_bottom_to_notification_settings.xml
index bec774b..2a75c14 100644
--- a/res/xml/swipe_bottom_to_notification_settings.xml
+++ b/res/xml/swipe_bottom_to_notification_settings.xml
@@ -20,11 +20,13 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/swipe_bottom_to_notifications_title">
- <com.android.settings.widget.VideoPreference
- android:title="@string/swipe_bottom_to_notifications_title"
- settings:animation="@raw/gesture_fingerprint_swipe"
- settings:preview="@drawable/gesture_fingerprint_swipe"
- settings:controller="com.android.settings.widget.VideoPreferenceController"/>
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="one_handed_header"
+ android:layout="@layout/one_handed_header"
+ android:persistent="false"
+ android:selectable="false"
+ settings:allowDividerBelow="false"
+ settings:searchable="false"/>
<SwitchPreference
android:key="gesture_swipe_bottom_to_notification"
diff --git a/res/xml/tether_prefs.xml b/res/xml/tether_prefs.xml
index 37194d9..d86f420 100644
--- a/res/xml/tether_prefs.xml
+++ b/res/xml/tether_prefs.xml
@@ -49,9 +49,9 @@
android:summary="@string/ethernet_tethering_subtext"
settings:keywords="@string/keywords_hotspot_tethering" />
- <Preference
+ <com.android.settingslib.widget.FooterPreference
android:key="disabled_on_data_saver"
- android:summary="@string/tether_settings_disabled_on_data_saver"
+ android:title="@string/tether_settings_disabled_on_data_saver"
android:selectable="false"
- settings:allowDividerAbove="true" />
+ settings:searchable="false"/>
</PreferenceScreen>
diff --git a/res/xml/top_level_settings.xml b/res/xml/top_level_settings.xml
index 78dfe9b..725390e 100644
--- a/res/xml/top_level_settings.xml
+++ b/res/xml/top_level_settings.xml
@@ -78,7 +78,7 @@
android:summary="@string/summary_placeholder"
android:icon="@drawable/ic_homepage_storage"
android:order="-60"
- android:fragment="com.android.settings.deviceinfo.StorageSettings"
+ android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>
<Preference
diff --git a/res/xml/top_level_settings_grouped.xml b/res/xml/top_level_settings_grouped.xml
index 704a106..df935d0 100644
--- a/res/xml/top_level_settings_grouped.xml
+++ b/res/xml/top_level_settings_grouped.xml
@@ -59,7 +59,7 @@
settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>
<com.android.settings.homepage.HomepagePreference
- android:fragment="com.android.settings.deviceinfo.StorageSettings"
+ android:fragment="com.android.settings.deviceinfo.StorageDashboardFragment"
android:icon="@drawable/ic_homepage_storage_v2"
android:key="top_level_storage"
android:order="-100"
diff --git a/res/xml/wifi_network_details_fragment2.xml b/res/xml/wifi_network_details_fragment2.xml
index 181b799..cb24a8f 100644
--- a/res/xml/wifi_network_details_fragment2.xml
+++ b/res/xml/wifi_network_details_fragment2.xml
@@ -101,6 +101,11 @@
android:key="ip_details_category"
android:title="@string/wifi_setup_detail">
<Preference
+ android:key="type"
+ android:title="@string/wifi_type_title"
+ android:selectable="false"
+ settings:enableCopying="true"/>
+ <Preference
android:key="ssid"
android:title="@string/wifi_advanced_ssid_title"
android:selectable="false"
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 0c1ace9..a276ad7 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -446,6 +446,13 @@
}
/**
+ * Returns the specified system service from the owning Activity.
+ */
+ protected <T> T getSystemService(final Class<T> serviceClass) {
+ return getActivity().getSystemService(serviceClass);
+ }
+
+ /**
* Returns the PackageManager from the owning Activity.
*/
protected PackageManager getPackageManager() {
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index 5f72ca5..80b97e4 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -34,7 +34,6 @@
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.util.Log;
@@ -74,6 +73,8 @@
implements View.OnClickListener {
private static final String TAG = "UserCredentialsSettings";
+ private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+
@Override
public int getMetricsCategory() {
return SettingsEnums.USER_CREDENTIALS;
@@ -210,15 +211,10 @@
private void deleteWifiCredential(final Credential credential) {
try {
- KeyStore keyStore = null;
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(
- new AndroidKeyStoreLoadStoreParameter(
- KeyProperties.NAMESPACE_WIFI));
- } else {
- keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
- }
+ final KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+ keyStore.load(
+ new AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.NAMESPACE_WIFI));
keyStore.deleteEntry(credential.getAlias());
} catch (Exception e) {
throw new RuntimeException("Failed to delete keys from keystore.");
@@ -278,18 +274,13 @@
final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
try {
- KeyStore processKeystore = KeyStore.getInstance("AndroidKeyStore");
+ KeyStore processKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
processKeystore.load(null);
KeyStore wifiKeystore = null;
if (myUserId == 0) {
- // Only the primary user may see wifi configurations.
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- wifiKeystore = KeyStore.getInstance("AndroidKeyStore");
- wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
- KeyProperties.NAMESPACE_WIFI));
- } else {
- wifiKeystore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
- }
+ wifiKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+ wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
+ KeyProperties.NAMESPACE_WIFI));
}
List<Credential> credentials = new ArrayList<>();
diff --git a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
index 82b3a64..bfe61b7 100644
--- a/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
+++ b/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceController.java
@@ -18,8 +18,8 @@
import android.content.Context;
import android.hardware.display.ColorDisplayManager;
-import android.provider.Settings;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/** PreferenceController that shows the Reduce Bright Colors summary */
@@ -32,8 +32,8 @@
@Override
public CharSequence getSummary() {
- return AccessibilityUtil.getSummary(mContext,
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
+ return mContext.getText(
+ R.string.reduce_bright_colors_preference_summary);
}
@Override
diff --git a/src/com/android/settings/accessibility/ShortcutPreference.java b/src/com/android/settings/accessibility/ShortcutPreference.java
index a9b542a..cff9117 100644
--- a/src/com/android/settings/accessibility/ShortcutPreference.java
+++ b/src/com/android/settings/accessibility/ShortcutPreference.java
@@ -63,7 +63,7 @@
super(context, attrs);
setLayoutResource(R.layout.accessibility_shortcut_secondary_action);
setWidgetLayoutResource(R.layout.preference_widget_primary_switch);
- setIconSpaceReserved(true);
+ setIconSpaceReserved(false);
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index cf9c08b..be6b141 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -488,7 +488,7 @@
// Show the "Settings" menu as if it were a preference screen.
mSettingsPreference = new Preference(getPrefContext());
mSettingsPreference.setTitle(mSettingsTitle);
- mSettingsPreference.setIconSpaceReserved(true);
+ mSettingsPreference.setIconSpaceReserved(false);
mSettingsPreference.setIntent(mSettingsIntent);
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
diff --git a/src/com/android/settings/accessibility/TurnScreenDarkerFragment.java b/src/com/android/settings/accessibility/TurnScreenDarkerFragment.java
index ad4bd56..0e0a600 100644
--- a/src/com/android/settings/accessibility/TurnScreenDarkerFragment.java
+++ b/src/com/android/settings/accessibility/TurnScreenDarkerFragment.java
@@ -94,8 +94,6 @@
if (ColorDisplayManager.isColorTransformAccelerated(getContext())) {
mToggleInversionPreference.setSummary(AccessibilityUtil.getSummary(
getContext(), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED));
- mReduceBrightColorsPreference.setSummary(AccessibilityUtil.getSummary(
- getContext(), Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED));
getPreferenceScreen().removePreference(experimentalCategory);
} else {
// Move following preferences to experimental category if device don't supports HWC
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index 7b50b46..9e232f4 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -27,6 +27,8 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.applications.defaultapps.DefaultAutofillPreferenceController;
+import com.android.settings.applications.defaultapps.DefaultWorkAutofillPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -68,14 +70,22 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ buildAutofillPreferenceControllers(context, controllers);
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
- return buildPreferenceControllers(context, this /* parent */, authorities);
+ buildAccountPreferenceControllers(context, this /* parent */, authorities, controllers);
+ return controllers;
}
- private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
- SettingsPreferenceFragment parent, String[] authorities) {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ static void buildAutofillPreferenceControllers(
+ Context context, List<AbstractPreferenceController> controllers) {
+ controllers.add(new DefaultAutofillPreferenceController(context));
+ controllers.add(new DefaultWorkAutofillPreferenceController(context));
+ }
+ private static void buildAccountPreferenceControllers(
+ Context context, SettingsPreferenceFragment parent, String[] authorities,
+ List<AbstractPreferenceController> controllers) {
final AccountPreferenceController accountPrefController =
new AccountPreferenceController(context, parent, authorities,
ProfileSelectFragment.ProfileType.ALL);
@@ -86,7 +96,6 @@
controllers.add(new AutoSyncDataPreferenceController(context, parent));
controllers.add(new AutoSyncPersonalDataPreferenceController(context, parent));
controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
- return controllers;
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
@@ -95,8 +104,11 @@
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
- return buildPreferenceControllers(
- context, null /* parent */, null /* authorities*/);
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ buildAccountPreferenceControllers(
+ context, null /* parent */, null /* authorities*/, controllers);
+ buildAutofillPreferenceControllers(context, controllers);
+ return controllers;
}
@Override
diff --git a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
index f29326e..c97c886 100644
--- a/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountPersonalDashboardFragment.java
@@ -18,6 +18,8 @@
import static android.provider.Settings.EXTRA_AUTHORITIES;
+import static com.android.settings.accounts.AccountDashboardFragment.buildAutofillPreferenceControllers;
+
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -61,14 +63,16 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ buildAutofillPreferenceControllers(context, controllers);
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
- return buildPreferenceControllers(context, this /* parent */, authorities);
+ buildAccountPreferenceControllers(context, this /* parent */, authorities, controllers);
+ return controllers;
}
- private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
- SettingsPreferenceFragment parent, String[] authorities) {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
-
+ private static void buildAccountPreferenceControllers(
+ Context context, SettingsPreferenceFragment parent, String[] authorities,
+ List<AbstractPreferenceController> controllers) {
final AccountPreferenceController accountPrefController =
new AccountPreferenceController(context, parent, authorities,
ProfileSelectFragment.ProfileType.PERSONAL);
@@ -78,7 +82,6 @@
controllers.add(accountPrefController);
controllers.add(new AutoSyncDataPreferenceController(context, parent));
controllers.add(new AutoSyncPersonalDataPreferenceController(context, parent));
- return controllers;
}
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
@@ -88,6 +91,7 @@
// @Override
// public List<AbstractPreferenceController> createPreferenceControllers(
// Context context) {
+// ..Add autofill here too..
// return buildPreferenceControllers(
// context, null /* parent */, null /* authorities*/);
// }
diff --git a/src/com/android/settings/accounts/AccountPreferenceController.java b/src/com/android/settings/accounts/AccountPreferenceController.java
index ff5bc78..64c2d13 100644
--- a/src/com/android/settings/accounts/AccountPreferenceController.java
+++ b/src/com/android/settings/accounts/AccountPreferenceController.java
@@ -75,11 +75,12 @@
private static final String TAG = "AccountPrefController";
- private static final int ORDER_ACCOUNT_PROFILES = 1;
+ private static final int ORDER_ACCOUNT_PROFILES = 101;
private static final int ORDER_LAST = 1002;
private static final int ORDER_NEXT_TO_LAST = 1001;
private static final int ORDER_NEXT_TO_NEXT_TO_LAST = 1000;
+ private static final String PREF_KEY_ACCOUNTS = "accounts_category";
private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
private static final String PREF_KEY_REMOVE_PROFILE = "remove_profile";
private static final String PREF_KEY_WORK_PROFILE_SETTING = "work_profile_setting";
@@ -348,8 +349,10 @@
}
}
final PreferenceScreen screen = mFragment.getPreferenceScreen();
- if (screen != null) {
- screen.addPreference(preferenceGroup);
+ final PreferenceGroup accounts =
+ screen == null ? null : screen.findPreference(PREF_KEY_ACCOUNTS);
+ if (accounts != null) {
+ accounts.addPreference(preferenceGroup);
}
profileData.preferenceGroup = preferenceGroup;
if (userInfo.isEnabled()) {
diff --git a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
index 853c66b..4e6515b 100644
--- a/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountWorkProfileDashboardFragment.java
@@ -18,6 +18,8 @@
import static android.provider.Settings.EXTRA_AUTHORITIES;
+import static com.android.settings.accounts.AccountDashboardFragment.buildAutofillPreferenceControllers;
+
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -61,14 +63,16 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ buildAutofillPreferenceControllers(context, controllers);
final String[] authorities = getIntent().getStringArrayExtra(EXTRA_AUTHORITIES);
- return buildPreferenceControllers(context, this /* parent */, authorities);
+ buildAccountPreferenceControllers(context, this /* parent */, authorities, controllers);
+ return controllers;
}
- private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
- SettingsPreferenceFragment parent, String[] authorities) {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
-
+ private static void buildAccountPreferenceControllers(
+ Context context, SettingsPreferenceFragment parent, String[] authorities,
+ List<AbstractPreferenceController> controllers) {
final AccountPreferenceController accountPrefController =
new AccountPreferenceController(context, parent, authorities,
ProfileSelectFragment.ProfileType.WORK);
@@ -78,7 +82,6 @@
controllers.add(accountPrefController);
controllers.add(new AutoSyncDataPreferenceController(context, parent));
controllers.add(new AutoSyncWorkDataPreferenceController(context, parent));
- return controllers;
}
// TODO: b/141601408. After featureFlag settings_work_profile is launched, unmark this
@@ -88,6 +91,7 @@
// @Override
// public List<AbstractPreferenceController> createPreferenceControllers(
// Context context) {
+// ..Add autofill here too..
// return buildPreferenceControllers(
// context, null /* parent */, null /* authorities*/);
// }
diff --git a/src/com/android/settings/accounts/AvatarViewMixin.java b/src/com/android/settings/accounts/AvatarViewMixin.java
index 7eb8cab..c4ab55a 100644
--- a/src/com/android/settings/accounts/AvatarViewMixin.java
+++ b/src/com/android/settings/accounts/AvatarViewMixin.java
@@ -17,7 +17,6 @@
package com.android.settings.accounts;
import android.accounts.Account;
-import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
@@ -65,14 +64,23 @@
private final Context mContext;
private final ImageView mAvatarView;
private final MutableLiveData<Bitmap> mAvatarImage;
- private final ActivityManager mActivityManager;
@VisibleForTesting
String mAccountName;
+ /**
+ * @return true if the avatar icon is supported.
+ */
+ public static boolean isAvatarSupported(Context context) {
+ if (!context.getResources().getBoolean(R.bool.config_show_avatar_in_homepage)) {
+ Log.d(TAG, "Feature disabled by config. Skipping");
+ return false;
+ }
+ return true;
+ }
+
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
mContext = activity.getApplicationContext();
- mActivityManager = mContext.getSystemService(ActivityManager.class);
mAvatarView = avatarView;
mAvatarView.setOnClickListener(v -> {
Intent intent;
@@ -117,14 +125,6 @@
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
- if (!mContext.getResources().getBoolean(R.bool.config_show_avatar_in_homepage)) {
- Log.d(TAG, "Feature disabled by config. Skipping");
- return;
- }
- if (mActivityManager.isLowRamDevice()) {
- Log.d(TAG, "Feature disabled on low ram device. Skipping");
- return;
- }
if (hasAccount()) {
loadAccount();
} else {
diff --git a/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java b/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java
index 7cd746f..174ef0f 100644
--- a/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java
+++ b/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceController.java
@@ -17,19 +17,11 @@
package com.android.settings.accounts;
import android.content.Context;
-import android.icu.text.ListFormatter;
-import android.os.UserHandle;
-import android.text.BidiFormatter;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
-import com.android.settingslib.accounts.AuthenticatorHelper;
-
-import java.util.ArrayList;
-import java.util.List;
public class TopLevelAccountEntryPreferenceController extends BasePreferenceController {
public TopLevelAccountEntryPreferenceController(Context context, String preferenceKey) {
@@ -47,29 +39,6 @@
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.SILKY_HOME)) {
return null;
}
-
- final AuthenticatorHelper authHelper = new AuthenticatorHelper(mContext,
- UserHandle.of(UserHandle.myUserId()), null /* OnAccountsUpdateListener */);
- final String[] types = authHelper.getEnabledAccountTypes();
- final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
- final List<CharSequence> summaries = new ArrayList<>();
-
- if (types == null || types.length == 0) {
- summaries.add(mContext.getString(R.string.account_dashboard_default_summary));
- } else {
- // Show up to 3 account types, ignore any null value
- int accountToAdd = Math.min(3, types.length);
-
- for (int i = 0; i < types.length && accountToAdd > 0; i++) {
- final CharSequence label = authHelper.getLabelForType(mContext, types[i]);
- if (TextUtils.isEmpty(label)) {
- continue;
- }
-
- summaries.add(bidiFormatter.unicodeWrap(label));
- accountToAdd--;
- }
- }
- return ListFormatter.getInstance().format(summaries);
+ return mContext.getString(R.string.account_dashboard_default_summary);
}
}
diff --git a/src/com/android/settings/applications/AppLaunchSettings.java b/src/com/android/settings/applications/AppLaunchSettings.java
deleted file mode 100644
index 86bc7ed..0000000
--- a/src/com/android/settings/applications/AppLaunchSettings.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications;
-
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
-
-import android.app.settings.SettingsEnums;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.ArraySet;
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settingslib.applications.AppUtils;
-
-public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
- Preference.OnPreferenceChangeListener {
- private static final String TAG = "AppLaunchSettings";
- private static final String KEY_APP_LINK_STATE = "app_link_state";
- private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
- private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
- private static final String FRAGMENT_OPEN_SUPPORTED_LINKS =
- "com.android.settings.applications.OpenSupportedLinks";
-
- private PackageManager mPm;
-
- private boolean mIsBrowser;
- private boolean mHasDomainUrls;
- private Preference mAppLinkState;
- private AppDomainsPreference mAppDomainUrls;
- private ClearDefaultsPreference mClearDefaultsPreference;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.installed_app_launch_settings);
- mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
- mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
- mAppLinkState = findPreference(KEY_APP_LINK_STATE);
- mAppLinkState.setOnPreferenceClickListener(preference -> {
- final Bundle args = new Bundle();
- args.putString(ARG_PACKAGE_NAME, mPackageName);
- args.putInt(ARG_PACKAGE_UID, mUserId);
-
- new SubSettingLauncher(this.getContext())
- .setDestination(FRAGMENT_OPEN_SUPPORTED_LINKS)
- .setArguments(args)
- .setSourceMetricsCategory(SettingsEnums.APPLICATIONS_APP_LAUNCH)
- .setTitleRes(-1)
- .launch();
- return true;
- });
-
- mPm = getActivity().getPackageManager();
-
- mIsBrowser = AppUtils.isBrowserApp(this.getContext(), mPackageName, UserHandle.myUserId());
- mHasDomainUrls =
- (mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
-
- if (!mIsBrowser) {
- CharSequence[] entries = getEntries(mPackageName);
- mAppDomainUrls.setTitles(entries);
- mAppDomainUrls.setValues(new int[entries.length]);
- mAppLinkState.setEnabled(mHasDomainUrls);
- } else {
- // Browsers don't show the app-link prefs
- mAppLinkState.setShouldDisableView(true);
- mAppLinkState.setEnabled(false);
- mAppDomainUrls.setShouldDisableView(true);
- mAppDomainUrls.setEnabled(false);
- }
- }
-
- private int linkStateToResourceId(int state) {
- switch (state) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
- return R.string.app_link_open_always; // Always
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
- return R.string.app_link_open_never; // Never
- default:
- return R.string.app_link_open_ask; // Ask
- }
- }
-
- private CharSequence[] getEntries(String packageName) {
- ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
- return result.toArray(new CharSequence[result.size()]);
- }
-
- private void setAppLinkStateSummary() {
- final int state = mPm.getIntentVerificationStatusAsUser(mPackageName,
- UserHandle.myUserId());
- mAppLinkState.setSummary(linkStateToResourceId(state));
- }
-
- @Override
- protected boolean refreshUi() {
- if (mHasDomainUrls) {
- //Update the summary after return from the OpenSupportedLinks
- setAppLinkStateSummary();
- }
- mClearDefaultsPreference.setPackageName(mPackageName);
- mClearDefaultsPreference.setAppEntry(mAppEntry);
- return true;
- }
-
- @Override
- protected AlertDialog createDialog(int id, int errorCode) {
- // No dialogs for preferred launch settings.
- return null;
- }
-
- @Override
- public void onClick(View v) {
- // Nothing to do
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- // actual updates are handled by the app link dropdown callback
- return true;
- }
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.APPLICATIONS_APP_LAUNCH;
- }
-}
diff --git a/src/com/android/settings/applications/ClearDefaultsPreference.java b/src/com/android/settings/applications/ClearDefaultsPreference.java
index f946636..0952b9c 100644
--- a/src/com/android/settings/applications/ClearDefaultsPreference.java
+++ b/src/com/android/settings/applications/ClearDefaultsPreference.java
@@ -117,7 +117,7 @@
if (mUsbManager != null) {
final int userId = UserHandle.myUserId();
mPm.clearPackagePreferredActivities(mPackageName);
- if (isDefaultBrowser(mPackageName)) {
+ if (AppUtils.isDefaultBrowser(getContext(), mPackageName)) {
mPm.setDefaultBrowserPackageNameAsUser(null, userId);
}
try {
@@ -141,7 +141,7 @@
TextView autoLaunchView = (TextView) view.findViewById(R.id.auto_launch);
boolean autoLaunchEnabled = AppUtils.hasPreferredActivities(mPm, mPackageName)
- || isDefaultBrowser(mPackageName)
+ || AppUtils.isDefaultBrowser(getContext(), mPackageName)
|| AppUtils.hasUsbDefaults(mUsbManager, mPackageName);
if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
resetLaunchDefaultsUi(autoLaunchView);
@@ -185,11 +185,6 @@
return true;
}
- private boolean isDefaultBrowser(String packageName) {
- final String defaultBrowser = mPm.getDefaultBrowserPackageNameAsUser(UserHandle.myUserId());
- return packageName.equals(defaultBrowser);
- }
-
private void resetLaunchDefaultsUi(TextView autoLaunchView) {
autoLaunchView.setText(R.string.auto_launch_disable_text);
// Disable clear activities button
diff --git a/src/com/android/settings/applications/HibernatedAppsPreferenceController.java b/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
index 40cbb2e..4088d3a 100644
--- a/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
+++ b/src/com/android/settings/applications/HibernatedAppsPreferenceController.java
@@ -20,15 +20,19 @@
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
+import android.apphibernation.AppHibernationManager;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
+import java.util.List;
+
/**
* A preference controller handling the logic for updating summary of hibernated apps.
- * TODO(b/181172051): add intent to launch Auto Revoke UI in app_and_notification.xml
*/
public final class HibernatedAppsPreferenceController extends BasePreferenceController {
private static final String TAG = "HibernatedAppsPrefController";
@@ -39,7 +43,8 @@
@Override
public int getAvailabilityStatus() {
- return isHibernationEnabled() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ return isHibernationEnabled() && getNumHibernated() > 0
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
@@ -50,8 +55,27 @@
}
private int getNumHibernated() {
- //TODO(b/181172051): hook into hibernation service to get the number of hibernated apps.
- return 0;
+ final PackageManager pm = mContext.getPackageManager();
+ final AppHibernationManager ahm = mContext.getSystemService(AppHibernationManager.class);
+ final List<String> hibernatedPackages = ahm.getHibernatingPackagesForUser();
+ int numHibernated = hibernatedPackages.size();
+
+ // Also need to count packages that are auto revoked but not hibernated.
+ final List<PackageInfo> packages = pm.getInstalledPackages(
+ PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_PERMISSIONS);
+ for (PackageInfo pi : packages) {
+ final String packageName = pi.packageName;
+ if (!hibernatedPackages.contains(packageName) && pi.requestedPermissions != null) {
+ for (String perm : pi.requestedPermissions) {
+ if ((pm.getPermissionFlags(perm, packageName, mContext.getUser())
+ & PackageManager.FLAG_PERMISSION_AUTO_REVOKED) != 0) {
+ numHibernated++;
+ break;
+ }
+ }
+ }
+ }
+ return numHibernated;
}
private static boolean isHibernationEnabled() {
diff --git a/src/com/android/settings/applications/InstalledAppOpenByDefaultActivity.java b/src/com/android/settings/applications/InstalledAppOpenByDefaultActivity.java
index cd30d79..799389e 100644
--- a/src/com/android/settings/applications/InstalledAppOpenByDefaultActivity.java
+++ b/src/com/android/settings/applications/InstalledAppOpenByDefaultActivity.java
@@ -19,6 +19,7 @@
import android.content.Intent;
import com.android.settings.SettingsActivity;
+import com.android.settings.applications.intentpicker.AppLaunchSettings;
public class InstalledAppOpenByDefaultActivity extends SettingsActivity {
diff --git a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
index f9c88fc..d8c190f 100644
--- a/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceController.java
@@ -28,7 +28,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppLaunchSettings;
+import com.android.settings.applications.intentpicker.AppLaunchSettings;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
diff --git a/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceController.java b/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceController.java
index 8ab2c9d..40be629 100644
--- a/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceController.java
@@ -80,7 +80,8 @@
/**
* Set the package. And also retrieve details from package manager. Some packages may be
- * exempted from hibernation by default.
+ * exempted from hibernation by default. This method should only be called to initialize the
+ * controller.
* @param packageName The name of the package whose hibernation state to be managed.
*/
void setPackage(@NonNull String packageName) {
@@ -93,8 +94,7 @@
? android.os.Build.VERSION_CODES.R
: android.os.Build.VERSION_CODES.Q;
try {
- mPackageUid = packageManager.getPackageUidAsUser(
- packageName, mContext.getUserId());
+ mPackageUid = packageManager.getPackageUid(packageName, /* flags */ 0);
mIsPackageExemptByDefault = packageManager.getTargetSdkVersion(packageName)
<= maxTargetSdkVersionForExemptApps;
mIsPackageSet = true;
diff --git a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
index f27530e..47882de 100644
--- a/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
+++ b/src/com/android/settings/applications/autofill/PasswordsPreferenceController.java
@@ -16,37 +16,64 @@
package com.android.settings.applications.autofill;
+import static android.service.autofill.AutofillService.EXTRA_RESULT;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_CREATE;
+import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY;
+
import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
import android.util.IconDrawableFactory;
+import android.util.Log;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
+import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
+import java.lang.ref.WeakReference;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Queries available autofill services and adds preferences for those that declare passwords
* settings.
+ * <p>
+ * The controller binds to each service to fetch the number of saved passwords in each.
*/
-public class PasswordsPreferenceController extends BasePreferenceController {
+public class PasswordsPreferenceController extends BasePreferenceController
+ implements LifecycleObserver {
+ private static final String TAG = "AutofillSettings";
private final PackageManager mPm;
private final IconDrawableFactory mIconFactory;
private final List<AutofillServiceInfo> mServices;
+ private LifecycleOwner mLifecycleOwner;
+
public PasswordsPreferenceController(Context context, String preferenceKey) {
this(context, preferenceKey,
AutofillServiceInfo.getAvailableServices(context, UserHandle.myUserId()));
@@ -67,6 +94,11 @@
mServices = availableServices;
}
+ @OnLifecycleEvent(ON_CREATE)
+ void onCreate(LifecycleOwner lifecycleOwner) {
+ mLifecycleOwner = lifecycleOwner;
+ }
+
@Override
public int getAvailabilityStatus() {
return mServices.isEmpty() ? CONDITIONALLY_UNAVAILABLE : AVAILABLE;
@@ -96,7 +128,83 @@
pref.setIntent(
new Intent(Intent.ACTION_MAIN)
.setClassName(serviceInfo.packageName, service.getPasswordsActivity()));
+
+ final MutableLiveData<Integer> passwordCount = new MutableLiveData<>();
+ passwordCount.observe(
+ mLifecycleOwner, count -> {
+ // TODO(b/169455298): Validate the result.
+ final CharSequence summary =
+ mContext.getResources().getQuantityString(
+ R.plurals.autofill_passwords_count, count, count);
+ pref.setSummary(summary);
+ });
+ // TODO(b/169455298): Limit the number of concurrent queries.
+ // TODO(b/169455298): Cache the results for some time.
+ requestSavedPasswordCount(service, user, passwordCount);
+
group.addPreference(pref);
}
}
+
+ private void requestSavedPasswordCount(
+ AutofillServiceInfo service, @UserIdInt int user, MutableLiveData<Integer> data) {
+ final Intent intent =
+ new Intent(AutofillService.SERVICE_INTERFACE)
+ .setComponent(service.getServiceInfo().getComponentName());
+ final AutofillServiceConnection connection = new AutofillServiceConnection(mContext, data);
+ if (mContext.bindServiceAsUser(
+ intent, connection, Context.BIND_AUTO_CREATE, UserHandle.of(user))) {
+ connection.mBound.set(true);
+ mLifecycleOwner.getLifecycle().addObserver(connection);
+ }
+ }
+
+ private static class AutofillServiceConnection implements ServiceConnection, LifecycleObserver {
+ final WeakReference<Context> mContext;
+ final MutableLiveData<Integer> mData;
+ final AtomicBoolean mBound = new AtomicBoolean();
+
+ AutofillServiceConnection(Context context, MutableLiveData<Integer> data) {
+ mContext = new WeakReference<>(context);
+ mData = data;
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ final IAutoFillService autofillService = IAutoFillService.Stub.asInterface(service);
+ // TODO check if debug is logged on user build.
+ Log.d(TAG, "Fetching password count from " + name);
+ try {
+ autofillService.onSavedPasswordCountRequest(
+ new IResultReceiver.Stub() {
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ Log.d(TAG, "Received password count result " + resultCode
+ + " from " + name);
+ if (resultCode == 0 && resultData != null) {
+ mData.postValue(resultData.getInt(EXTRA_RESULT));
+ }
+ unbind();
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to fetch password count: " + e);
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+
+ @OnLifecycleEvent(ON_DESTROY)
+ void unbind() {
+ if (!mBound.getAndSet(false)) {
+ return;
+ }
+ final Context context = mContext.get();
+ if (context != null) {
+ context.unbindService(this);
+ }
+ }
+ }
}
diff --git a/src/com/android/settings/applications/defaultapps/AutofillPicker.java b/src/com/android/settings/applications/defaultapps/AutofillPicker.java
deleted file mode 100644
index 62a478f..0000000
--- a/src/com/android/settings/applications/defaultapps/AutofillPicker.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2018 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.defaultapps;
-
-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.core.AbstractPreferenceController;
-import com.android.settingslib.search.SearchIndexable;
-
-import java.util.Arrays;
-import java.util.List;
-
-@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class AutofillPicker extends DashboardFragment {
- private static final String TAG = "AutofillPicker";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DEFAULT_AUTOFILL_PICKER;
- }
-
- @Override
- protected String getLogTag() {
- return TAG;
- }
-
- @Override
- protected int getPreferenceScreenResId() {
- return R.xml.default_autofill_picker_settings;
- }
-
- @Override
- protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
- return buildPreferenceControllers(context);
- }
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.default_autofill_picker_settings) {
-
- @Override
- public List<AbstractPreferenceController> getPreferenceControllers(Context
- context) {
- return buildPreferenceControllers(context);
- }
- };
-
- private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
- return Arrays.asList(
- new DefaultAutofillPreferenceController(context),
- new DefaultWorkAutofillPreferenceController(context));
- }
-}
diff --git a/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java b/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
new file mode 100644
index 0000000..a277169
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/AppLaunchSettings.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications.intentpicker;
+
+import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE;
+import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_SELECTED;
+import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_VERIFIED;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationUserState;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.applications.AppInfoBase;
+import com.android.settings.applications.ClearDefaultsPreference;
+import com.android.settings.utils.AnnotationSpan;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.widget.FooterPreference;
+import com.android.settingslib.widget.MainSwitchPreference;
+import com.android.settingslib.widget.OnMainSwitchChangeListener;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/** The page of the Open by default */
+public class AppLaunchSettings extends AppInfoBase implements
+ Preference.OnPreferenceChangeListener, OnMainSwitchChangeListener {
+ private static final String TAG = "AppLaunchSettings";
+ // Preference keys
+ private static final String MAIN_SWITCH_PREF_KEY = "open_by_default_supported_links";
+ private static final String VERIFIED_LINKS_PREF_KEY = "open_by_default_verified_links";
+ private static final String ADD_LINK_PREF_KEY = "open_by_default_add_link";
+ private static final String CLEAR_DEFAULTS_PREF_KEY = "app_launch_clear_defaults";
+ private static final String FOOTER_PREF_KEY = "open_by_default_footer";
+
+ private static final String MAIN_PREF_CATEGORY_KEY = "open_by_default_main_category";
+ private static final String SELECTED_LINKS_CATEGORY_KEY =
+ "open_by_default_selected_links_category";
+ private static final String OTHER_DETAILS_PREF_CATEGORY_KEY = "app_launch_other_defaults";
+
+ // Url and Uri
+ private static final String ANNOTATION_URL = "url";
+ private static final String LEARN_MORE_URI =
+ "https://developer.android.com/training/app-links/verify-site-associations";
+
+ // Dialogs id
+ private static final int DLG_VERIFIED_LINKS = DLG_BASE + 1;
+
+ // Arguments key
+ public static final String APP_PACKAGE_KEY = "app_package";
+
+ private ClearDefaultsPreference mClearDefaultsPreference;
+ private MainSwitchPreference mMainSwitchPreference;
+ private Preference mAddLinkPreference;
+ private PreferenceCategory mMainPreferenceCategory;
+ private PreferenceCategory mSelectedLinksPreferenceCategory;
+ private PreferenceCategory mOtherDefaultsPreferenceCategory;
+
+ private boolean mActivityCreated;
+
+ @VisibleForTesting
+ Context mContext;
+ @VisibleForTesting
+ DomainVerificationManager mDomainVerificationManager;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mContext = context;
+ mActivityCreated = false;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.installed_app_launch_settings);
+ mDomainVerificationManager = mContext.getSystemService(DomainVerificationManager.class);
+ initUIComponents();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ createHeaderPreference();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.APPLICATIONS_APP_LAUNCH;
+ }
+
+ @Override
+ protected AlertDialog createDialog(int id, int errorCode) {
+ if (id == DLG_VERIFIED_LINKS) {
+ return createVerifiedLinksDialog();
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean refreshUi() {
+ mClearDefaultsPreference.setPackageName(mPackageName);
+ mClearDefaultsPreference.setAppEntry(mAppEntry);
+ return true;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean isChecked = (boolean) newValue;
+ IntentPickerUtils.logd(
+ "onPreferenceChange: " + preference.getTitle() + " isChecked: " + isChecked);
+ if ((preference instanceof LeftSideCheckBoxPreference) && !isChecked) {
+ final Set<String> domainSet = new ArraySet<>();
+ domainSet.add(preference.getTitle().toString());
+ removePreference(preference.getKey());
+ final DomainVerificationUserState userState =
+ IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
+ mPackageName);
+ if (userState == null) {
+ return false;
+ }
+ setDomainVerificationUserSelection(userState.getIdentifier(), domainSet, /* enabled= */
+ false);
+ mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
+ }
+ return true;
+ }
+
+ @Override
+ public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ IntentPickerUtils.logd("onSwitchChanged: isChecked=" + isChecked);
+ if (mMainSwitchPreference != null) { //mMainSwitchPreference synced with Switch
+ mMainSwitchPreference.setChecked(isChecked);
+ }
+ if (mMainPreferenceCategory != null) {
+ mMainPreferenceCategory.setVisible(isChecked);
+ }
+ if (mDomainVerificationManager != null) {
+ try {
+ mDomainVerificationManager.setDomainVerificationLinkHandlingAllowed(mPackageName,
+ isChecked);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "onSwitchChanged: " + e.getMessage());
+ }
+ }
+ }
+
+ private void createHeaderPreference() {
+ if (mActivityCreated) {
+ Log.w(TAG, "onParentActivityCreated: ignoring duplicate call.");
+ return;
+ }
+ mActivityCreated = true;
+ if (mPackageInfo == null) {
+ Log.w(TAG, "onParentActivityCreated: PakcageInfo is null.");
+ return;
+ }
+ final Activity activity = getActivity();
+ final Preference pref = EntityHeaderController
+ .newInstance(activity, this, null /* header */)
+ .setRecyclerView(getListView(), getSettingsLifecycle())
+ .setIcon(Utils.getBadgedIcon(mContext, mPackageInfo.applicationInfo))
+ .setLabel(mPackageInfo.applicationInfo.loadLabel(mPm))
+ .setSummary("" /* summary */) // no version number
+ .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+ .setPackageName(mPackageName)
+ .setUid(mPackageInfo.applicationInfo.uid)
+ .setHasAppInfoLink(true)
+ .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
+ EntityHeaderController.ActionType.ACTION_NONE)
+ .done(activity, getPrefContext());
+ getPreferenceScreen().addPreference(pref);
+ }
+
+ private void initUIComponents() {
+ initMainSwitchAndCategories();
+ if (canUpdateMainSwitchAndCategories()) {
+ initVerifiedLinksPreference();
+ initAddLinkPreference();
+ addSelectedLinksPreference();
+ initFooter();
+ }
+ }
+
+ private void initMainSwitchAndCategories() {
+ mMainSwitchPreference = (MainSwitchPreference) findPreference(MAIN_SWITCH_PREF_KEY);
+ mMainPreferenceCategory = findPreference(MAIN_PREF_CATEGORY_KEY);
+ mSelectedLinksPreferenceCategory = findPreference(SELECTED_LINKS_CATEGORY_KEY);
+ // Initialize the "Other Default Category" section
+ initOtherDefaultsSection();
+ }
+
+ private boolean canUpdateMainSwitchAndCategories() {
+ final DomainVerificationUserState userState =
+ IntentPickerUtils.getDomainVerificationUserState(mDomainVerificationManager,
+ mPackageName);
+ if (userState == null) {
+ disabledPreference();
+ return false;
+ }
+
+ IntentPickerUtils.logd("isLinkHandlingAllowed() : " + userState.isLinkHandlingAllowed());
+ mMainSwitchPreference.updateStatus(userState.isLinkHandlingAllowed());
+ mMainSwitchPreference.addOnSwitchChangeListener(this);
+ mMainPreferenceCategory.setVisible(userState.isLinkHandlingAllowed());
+ return true;
+ }
+
+ /** Initialize verified links preference */
+ private void initVerifiedLinksPreference() {
+ final VerifiedLinksPreference verifiedLinksPreference =
+ (VerifiedLinksPreference) mMainPreferenceCategory.findPreference(
+ VERIFIED_LINKS_PREF_KEY);
+ verifiedLinksPreference.setWidgetFrameClickListener(l -> {
+ showVerifiedLinksDialog();
+ });
+ final int verifiedLinksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
+ verifiedLinksPreference.setTitle(getVerifiedLinksTitle(verifiedLinksNo));
+ verifiedLinksPreference.setCheckBoxVisible(verifiedLinksNo > 0);
+ verifiedLinksPreference.setEnabled(verifiedLinksNo > 0);
+ }
+
+ private void showVerifiedLinksDialog() {
+ final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
+ if (linksNo == 0) {
+ return;
+ }
+ showDialogInner(DLG_VERIFIED_LINKS, /* moveErrorCode= */ 0);
+ }
+
+ private AlertDialog createVerifiedLinksDialog() {
+ final int linksNo = getLinksNumber(DOMAIN_STATE_VERIFIED);
+
+ final View titleView = LayoutInflater.from(mContext).inflate(
+ R.layout.app_launch_verified_links_title, /* root= */ null);
+ ((TextView) titleView.findViewById(R.id.dialog_title)).setText(
+ getVerifiedLinksTitle(linksNo));
+ ((TextView) titleView.findViewById(R.id.dialog_message)).setText(
+ getVerifiedLinksMessage(linksNo));
+
+ final List<String> verifiedLinksList = IntentPickerUtils.getLinksList(
+ mDomainVerificationManager, mPackageName, DOMAIN_STATE_VERIFIED);
+ return new AlertDialog.Builder(mContext)
+ .setCustomTitle(titleView)
+ .setCancelable(true)
+ .setItems(verifiedLinksList.toArray(new String[0]), /* listener= */ null)
+ .setPositiveButton(R.string.app_launch_dialog_ok, /* listener= */ null)
+ .create();
+ }
+
+ @VisibleForTesting
+ String getVerifiedLinksTitle(int linksNo) {
+ return getResources().getQuantityString(
+ R.plurals.app_launch_verified_links_title, linksNo, linksNo);
+ }
+
+ private String getVerifiedLinksMessage(int linksNo) {
+ return getResources().getQuantityString(
+ R.plurals.app_launch_verified_links_message, linksNo, linksNo);
+ }
+
+ /** Add selected links items */
+ void addSelectedLinksPreference() {
+ if (getLinksNumber(DOMAIN_STATE_SELECTED) == 0) {
+ return;
+ }
+ mSelectedLinksPreferenceCategory.removeAll();
+ final List<String> selectedLinks = IntentPickerUtils.getLinksList(
+ mDomainVerificationManager, mPackageName, DOMAIN_STATE_SELECTED);
+ for (String host : selectedLinks) {
+ generateCheckBoxPreference(mSelectedLinksPreferenceCategory, host);
+ }
+ mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
+ }
+
+ /** Initialize add link preference */
+ private void initAddLinkPreference() {
+ mAddLinkPreference = findPreference(ADD_LINK_PREF_KEY);
+ mAddLinkPreference.setEnabled(isAddLinksNotEmpty());
+ mAddLinkPreference.setOnPreferenceClickListener(preference -> {
+ final int stateNoneLinksNo = getLinksNumber(DOMAIN_STATE_NONE);
+ IntentPickerUtils.logd("The number of the state none links: " + stateNoneLinksNo);
+ if (stateNoneLinksNo > 0) {
+ showProgressDialogFragment();
+ }
+ return true;
+ });
+ }
+
+ private boolean isAddLinksNotEmpty() {
+ return getLinksNumber(DOMAIN_STATE_NONE) > 0;
+ }
+
+ private void showProgressDialogFragment() {
+ final Bundle args = new Bundle();
+ args.putString(APP_PACKAGE_KEY, mPackageName);
+ final ProgressDialogFragment dialogFragment = new ProgressDialogFragment();
+ dialogFragment.setArguments(args);
+ dialogFragment.showDialog(getActivity().getSupportFragmentManager());
+ }
+
+ private void disabledPreference() {
+ mMainSwitchPreference.updateStatus(false);
+ mMainSwitchPreference.setSelectable(false);
+ mMainSwitchPreference.setEnabled(false);
+ mMainPreferenceCategory.setVisible(false);
+ }
+
+ /** Init OTHER DEFAULTS category */
+ private void initOtherDefaultsSection() {
+ mOtherDefaultsPreferenceCategory = findPreference(OTHER_DETAILS_PREF_CATEGORY_KEY);
+ mOtherDefaultsPreferenceCategory.setVisible(isClearDefaultsEnabled());
+ mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(
+ CLEAR_DEFAULTS_PREF_KEY);
+ }
+
+ private void initFooter() {
+ // learn more
+ final AnnotationSpan.LinkInfo linkInfo =
+ new AnnotationSpan.LinkInfo(ANNOTATION_URL, v -> {
+ final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(LEARN_MORE_URI));
+ mContext.startActivity(intent);
+ });
+ final CharSequence footerText = mContext.getText(R.string.app_launch_footer);
+ final FooterPreference footerPreference = (FooterPreference) findPreference(
+ FOOTER_PREF_KEY);
+ footerPreference.setTitle(AnnotationSpan.linkify(footerText, linkInfo));
+ }
+
+ private boolean isClearDefaultsEnabled() {
+ final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+ final boolean hasBindAppWidgetPermission =
+ appWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
+
+ final boolean isAutoLaunchEnabled = AppUtils.hasPreferredActivities(mPm, mPackageName)
+ || AppUtils.isDefaultBrowser(mContext, mPackageName)
+ || AppUtils.hasUsbDefaults(mUsbManager, mPackageName);
+
+ IntentPickerUtils.logd("isClearDefaultsEnabled hasBindAppWidgetPermission : "
+ + hasBindAppWidgetPermission);
+ IntentPickerUtils.logd(
+ "isClearDefaultsEnabled isAutoLaunchEnabled : " + isAutoLaunchEnabled);
+ return (isAutoLaunchEnabled || hasBindAppWidgetPermission);
+ }
+
+ private void setDomainVerificationUserSelection(UUID identifier, Set<String> domainSet,
+ boolean isEnabled) {
+ try {
+ mDomainVerificationManager.setDomainVerificationUserSelection(identifier, domainSet,
+ isEnabled);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "addSelectedItems : " + e.getMessage());
+ }
+ }
+
+ private void generateCheckBoxPreference(PreferenceCategory parent, String title) {
+ final LeftSideCheckBoxPreference checkBoxPreference = new LeftSideCheckBoxPreference(
+ parent.getContext(), /* isChecked= */ true);
+ checkBoxPreference.setTitle(title);
+ checkBoxPreference.setOnPreferenceChangeListener(this);
+ checkBoxPreference.setKey(UUID.randomUUID().toString());
+ parent.addPreference(checkBoxPreference);
+ }
+
+ /** get the number of the specify links */
+ private int getLinksNumber(@DomainVerificationUserState.DomainState int state) {
+ final List<String> linkList = IntentPickerUtils.getLinksList(
+ mDomainVerificationManager, mPackageName, state);
+ if (linkList == null) {
+ return 0;
+ }
+ return linkList.size();
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/IntentPickerUtils.java b/src/com/android/settings/applications/intentpicker/IntentPickerUtils.java
new file mode 100644
index 0000000..5b14bc7
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/IntentPickerUtils.java
@@ -0,0 +1,98 @@
+/*
+ * 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.intentpicker;
+
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationUserState;
+import android.os.Build;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.style.AlignmentSpan;
+import android.util.Log;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** The common APIs for intent picker */
+public class IntentPickerUtils {
+ private static final String TAG = "IntentPickerUtils";
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+
+ private IntentPickerUtils() {
+ }
+
+ /**
+ * Gets the centralized title.
+ *
+ * @param title The title of the dialog box.
+ * @return The spannable string with centralized title.
+ */
+ public static SpannableString getCentralizedDialogTitle(String title) {
+ final SpannableString dialogTitle = new SpannableString(title);
+ dialogTitle.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), /* start= */
+ 0, title.length(), /* flags= */ 0);
+ return dialogTitle;
+ }
+
+ /**
+ * Gets the {@link DomainVerificationUserState} for specific application.
+ *
+ * @param manager The {@link DomainVerificationManager}.
+ * @param pkgName The package name of the target application.
+ */
+ public static DomainVerificationUserState getDomainVerificationUserState(
+ DomainVerificationManager manager, String pkgName) {
+ try {
+ final DomainVerificationUserState domainVerificationUserState =
+ manager.getDomainVerificationUserState(pkgName);
+ return domainVerificationUserState;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Gets the links list by {@link DomainVerificationUserState.DomainState}
+ *
+ * @param manager The {@link DomainVerificationManager}.
+ * @param pkgName The package name of the target application.
+ * @param state The user state you want to query.
+ * @return A links list.
+ */
+ public static List<String> getLinksList(DomainVerificationManager manager, String pkgName,
+ @DomainVerificationUserState.DomainState int state) {
+ final DomainVerificationUserState userStage = getDomainVerificationUserState(manager,
+ pkgName);
+ if (userStage == null) {
+ return null;
+ }
+ return userStage.getHostToStateMap()
+ .entrySet()
+ .stream()
+ .filter(it -> it.getValue() == state)
+ .map(it -> it.getKey())
+ .collect(Collectors.toList());
+ }
+
+ /** Logs the message in debug ROM. */
+ public static void logd(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/LeftSideCheckBoxPreference.java b/src/com/android/settings/applications/intentpicker/LeftSideCheckBoxPreference.java
new file mode 100644
index 0000000..fdb6d25
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/LeftSideCheckBoxPreference.java
@@ -0,0 +1,74 @@
+/*
+ * 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.intentpicker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.CheckBox;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settingslib.widget.TwoTargetPreference;
+
+/** This preference has a check box in the left side. */
+public class LeftSideCheckBoxPreference extends TwoTargetPreference {
+ private boolean mChecked;
+ private CheckBox mCheckBox;
+
+ public LeftSideCheckBoxPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ setLayoutResource(R.layout.preference_checkable_two_target);
+ }
+
+ public LeftSideCheckBoxPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public LeftSideCheckBoxPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LeftSideCheckBoxPreference(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public LeftSideCheckBoxPreference(Context context, boolean isChecked) {
+ super(context);
+ mChecked = isChecked;
+ setLayoutResource(R.layout.preference_checkable_two_target);
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ mCheckBox = (CheckBox) view.findViewById(com.android.internal.R.id.checkbox);
+ if (mCheckBox != null) {
+ mCheckBox.setChecked(mChecked);
+ }
+ }
+
+ @Override
+ protected void onClick() {
+ if (mCheckBox != null) {
+ mChecked = !mChecked;
+ mCheckBox.setChecked(mChecked);
+ callChangeListener(mChecked);
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java
new file mode 100644
index 0000000..f99775e
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/ProgressDialogFragment.java
@@ -0,0 +1,216 @@
+/*
+ * 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.intentpicker;
+
+import static android.content.pm.verify.domain.DomainVerificationUserState.DOMAIN_STATE_NONE;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.verify.domain.DomainOwner;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ProgressBar;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProviders;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** A customized {@link InstrumentedDialogFragment} with a progress bar. */
+public class ProgressDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "ProgressDialogFragment";
+ private static final String DLG_ID = "ProgressDialog";
+ private static final int PROGRESS_BAR_STEPPING_TIME = 20;
+
+ private ProgressAlertDialog mProgressAlertDialog;
+ private DomainVerificationManager mDomainVerificationManager;
+ private List<SupportedLinkWrapper> mSupportedLinkWrapperList;
+ private SupportedLinkViewModel mViewModel;
+ private Handler mHandle;
+ private String mPackage;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mViewModel = ViewModelProviders.of(this.getActivity()).get(SupportedLinkViewModel.class);
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mPackage = getArguments().getString(AppLaunchSettings.APP_PACKAGE_KEY);
+ mDomainVerificationManager = getActivity().getSystemService(
+ DomainVerificationManager.class);
+ mHandle = new Handler(Looper.getMainLooper());
+ mProgressAlertDialog = createProgressAlertDialog();
+ return mProgressAlertDialog;
+ }
+
+ private ProgressAlertDialog createProgressAlertDialog() {
+ final Context context = getActivity();
+ final ProgressAlertDialog progressDialog = new ProgressAlertDialog(context);
+ final String title = context.getResources().getString(
+ R.string.app_launch_checking_links_title);
+ progressDialog.setTitle(IntentPickerUtils.getCentralizedDialogTitle(title));
+ progressDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ context.getText(R.string.app_launch_dialog_cancel),
+ (dialog, which) -> {
+ if (which == DialogInterface.BUTTON_NEGATIVE) {
+ dialog.cancel();
+ }
+ });
+ progressDialog.setCanceledOnTouchOutside(true);
+ return progressDialog;
+ }
+
+ /** Display the {@link ProgressAlertDialog}. */
+ public void showDialog(FragmentManager manager) {
+ show(manager, DLG_ID);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ generateProgressAlertDialog();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mProgressAlertDialog != null && mProgressAlertDialog.isShowing()) {
+ mProgressAlertDialog.cancel();
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ /**
+ * To generate a progress alter dialog and invoke the supported links dialog.
+ */
+ private void generateProgressAlertDialog() {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final long start = SystemClock.elapsedRealtime();
+ queryLinksInBackground();
+ IntentPickerUtils.logd(
+ "queryLinksInBackground take time: " + (SystemClock.elapsedRealtime() - start));
+ if (mProgressAlertDialog.isShowing()) {
+ mHandle.post(() -> {
+ synchronized (mHandle) {
+ if (mProgressAlertDialog.isShowing()) {
+ mProgressAlertDialog.dismiss();
+ IntentPickerUtils.logd("mProgressAlertDialog.dismiss() and isShowing: "
+ + mProgressAlertDialog.isShowing());
+ launchSupportedLinksDialogFragment();
+ }
+ }
+ });
+ }
+ });
+ }
+
+ private void queryLinksInBackground() {
+ final List<String> links = IntentPickerUtils.getLinksList(mDomainVerificationManager,
+ mPackage, DOMAIN_STATE_NONE);
+ final int linksNo = links.size();
+ int index = 0;
+ mSupportedLinkWrapperList = new ArrayList<>();
+ for (String host : links) {
+ final List<DomainOwner> ownerList =
+ mDomainVerificationManager.getOwnersForDomain(host);
+ mSupportedLinkWrapperList.add(new SupportedLinkWrapper(getActivity(), host, ownerList));
+ index++;
+ // The cancel was clicked while progressing to collect data.
+ if (!mProgressAlertDialog.isShowing()) {
+ Log.w(TAG, "Exit the background thread!!!");
+ // clear buffer
+ mSupportedLinkWrapperList.clear();
+ break;
+ }
+ int progress = (int) (index * 100) / linksNo;
+ mHandle.post(() -> {
+ synchronized (mHandle) {
+ if (!mProgressAlertDialog.isShowing()) {
+ Log.w(TAG, "Exit the UI thread");
+ return;
+ }
+ mProgressAlertDialog.getProgressBar().setProgress(progress);
+ }
+ });
+ if (ownerList.size() == 0) {
+ SystemClock.sleep(PROGRESS_BAR_STEPPING_TIME);
+ }
+ }
+ IntentPickerUtils.logd("queryLinksInBackground : SupportedLinkWrapperList size="
+ + mSupportedLinkWrapperList.size());
+ Collections.sort(mSupportedLinkWrapperList);
+ }
+
+ private void launchSupportedLinksDialogFragment() {
+ if (mSupportedLinkWrapperList.size() > 0) {
+ mViewModel.setSupportedLinkWrapperList(mSupportedLinkWrapperList);
+ final Bundle args = new Bundle();
+ args.putString(AppLaunchSettings.APP_PACKAGE_KEY, mPackage);
+ final SupportedLinksDialogFragment dialogFragment = new SupportedLinksDialogFragment();
+ dialogFragment.setArguments(args);
+ dialogFragment.showDialog(getActivity().getSupportFragmentManager());
+ }
+ }
+
+ /** Create a custom {@link AlertDialog} with a {@link ProgressBar}. */
+ static class ProgressAlertDialog extends AlertDialog {
+ private ProgressBar mProgressBar;
+
+ protected ProgressAlertDialog(@NonNull Context context) {
+ this(context, 0);
+ }
+
+ protected ProgressAlertDialog(@NonNull Context context, int themeResId) {
+ super(context, themeResId);
+ init(context);
+ }
+
+ private void init(Context context) {
+ final View view = LayoutInflater.from(context).inflate(
+ R.layout.app_launch_progress, /* root= */ null);
+ mProgressBar = view.findViewById(R.id.scan_links_progressbar);
+ mProgressBar.setProgress(0);
+ mProgressBar.setMax(100);
+ setView(view);
+ }
+
+ ProgressBar getProgressBar() {
+ return mProgressBar;
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinkViewModel.java b/src/com/android/settings/applications/intentpicker/SupportedLinkViewModel.java
new file mode 100644
index 0000000..c374b8c
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinkViewModel.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.applications.intentpicker;
+
+import android.app.Application;
+
+import androidx.lifecycle.AndroidViewModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This {@link AndroidViewModel} provides supported link wrapper data
+ * between multiple fragments.
+ */
+public class SupportedLinkViewModel extends AndroidViewModel {
+ private List<SupportedLinkWrapper> mSupportedLinkWrapperList;
+
+ public SupportedLinkViewModel(Application application) {
+ super(application);
+ }
+
+ /** Clear the list buffer of the {@link SupportedLinkWrapper}. */
+ public void clearSupportedLinkWrapperList() {
+ mSupportedLinkWrapperList = new ArrayList<>();
+ }
+
+ /** Set the list buffer of the {@link SupportedLinkWrapper}. */
+ public void setSupportedLinkWrapperList(List<SupportedLinkWrapper> wrapperList) {
+ mSupportedLinkWrapperList = wrapperList;
+ }
+
+ /** Get the list buffer of the {@link SupportedLinkWrapper}. */
+ public List<SupportedLinkWrapper> getSupportedLinkWrapperList() {
+ return mSupportedLinkWrapperList;
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java
new file mode 100644
index 0000000..0acc2bc
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.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.applications.intentpicker;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainOwner;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.settings.R;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A buffer of the supported link data. This {@link SupportedLinkWrapper} wraps the host, enabled
+ * and a list of {@link DomainOwner}.
+ */
+public class SupportedLinkWrapper implements Comparable {
+ private static final String TAG = "SupportedLinkWrapper";
+
+ private String mHost;
+ private List<DomainOwner> mOwnerList;
+ private boolean mIsEnabled;
+ private String mLastOwnerName;
+ private boolean mIsChecked;
+
+ public SupportedLinkWrapper(Context context, String host, List<DomainOwner> ownerList) {
+ mHost = host;
+ mOwnerList = ownerList;
+ mIsEnabled = true;
+ mLastOwnerName = "";
+ mIsChecked = false;
+ init(context);
+ }
+
+ private void init(Context context) {
+ if (mOwnerList.size() > 0) {
+ final long nonOverirideableNo = mOwnerList.stream()
+ .filter(it -> !it.isOverrideable())
+ .count();
+ mIsEnabled = (nonOverirideableNo == 0L);
+ if (nonOverirideableNo > 0L) {
+ mLastOwnerName = getLastPackageLabel(context, false);
+ } else {
+ mLastOwnerName = getLastPackageLabel(context, true);
+ }
+ }
+ }
+
+ private String getLastPackageLabel(Context context, boolean isOverrideable) {
+ final List<String> labelList = mOwnerList.stream()
+ .filter(it -> it.isOverrideable() == isOverrideable)
+ .map(it -> getLabel(context, it.getPackageName()))
+ .filter(label -> label != null)
+ .collect(Collectors.toList());
+ return labelList.get(labelList.size() - 1);
+ }
+
+ private String getLabel(Context context, String pkg) {
+ try {
+ final PackageManager pm = context.getPackageManager();
+ return pm.getApplicationInfo(pkg, /* flags= */ 0).loadLabel(pm).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "getLabel error : " + e.getMessage());
+ return null;
+ }
+ }
+
+ /** Returns the enabled/disabled value for list item. */
+ public boolean isEnabled() {
+ return mIsEnabled;
+ }
+
+ /** Returns the display format of list item in the Supported Links dialog */
+ public String getDisplayTitle(Context context) {
+ if (TextUtils.isEmpty(mLastOwnerName) || context == null) {
+ return mHost;
+ }
+ return mHost + System.lineSeparator() + context.getString(
+ R.string.app_launch_supported_links_subtext, mLastOwnerName);
+ }
+
+ /** Returns the host name. */
+ public String getHost() {
+ return mHost;
+ }
+
+ /** Returns the checked value for list item. */
+ public boolean isChecked() {
+ return mIsChecked;
+ }
+
+ /** Set the checked value. */
+ public void setChecked(boolean isChecked) {
+ mIsChecked = isChecked;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ final SupportedLinkWrapper that = (SupportedLinkWrapper) o;
+ if (this.mIsEnabled != that.mIsEnabled) {
+ return this.mIsEnabled ? -1 : 1;
+ }
+ if (TextUtils.isEmpty(this.mLastOwnerName) != TextUtils.isEmpty(that.mLastOwnerName)) {
+ return TextUtils.isEmpty(this.mLastOwnerName) ? -1 : 1;
+ }
+ return 0;
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinksAdapter.java b/src/com/android/settings/applications/intentpicker/SupportedLinksAdapter.java
new file mode 100644
index 0000000..9288d52
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinksAdapter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.intentpicker;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckedTextView;
+
+import com.android.settings.R;
+
+import java.util.List;
+
+/** This adapter is for supported links dialog. */
+public class SupportedLinksAdapter extends BaseAdapter {
+ private final Context mContext;
+ private final List<SupportedLinkWrapper> mWrapperList;
+
+ public SupportedLinksAdapter(Context context, List<SupportedLinkWrapper> list) {
+ mContext = context;
+ mWrapperList = list;
+ }
+
+ @Override
+ public int getCount() {
+ return mWrapperList.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position < mWrapperList.size()) {
+ return mWrapperList.get(position);
+ }
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = LayoutInflater.from(mContext).inflate(
+ R.layout.supported_links_dialog_item, /* root= */ null);
+ }
+ final CheckedTextView textView = convertView.findViewById(android.R.id.text1);
+ textView.setText(mWrapperList.get(position).getDisplayTitle(mContext));
+ textView.setEnabled(mWrapperList.get(position).isEnabled());
+ textView.setChecked(mWrapperList.get(position).isChecked());
+ textView.setOnClickListener(l -> {
+ textView.toggle();
+ mWrapperList.get(position).setChecked(textView.isChecked());
+ });
+ return convertView;
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java b/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java
new file mode 100644
index 0000000..db93df2
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinksDialogFragment.java
@@ -0,0 +1,134 @@
+/*
+ * 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.intentpicker;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.verify.domain.DomainVerificationManager;
+import android.content.pm.verify.domain.DomainVerificationUserState;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.ViewModelProviders;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+
+/** A customized {@link InstrumentedDialogFragment} with multiple checkboxes. */
+public class SupportedLinksDialogFragment extends InstrumentedDialogFragment {
+ private static final String TAG = "SupportedLinksDialogFrg";
+ private static final String DLG_ID = "SupportedLinksDialog";
+
+ private SupportedLinkViewModel mViewModel;
+ private List<SupportedLinkWrapper> mSupportedLinkWrapperList;
+ private String mPackage;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mPackage = getArguments().getString(AppLaunchSettings.APP_PACKAGE_KEY);
+ mViewModel = ViewModelProviders.of(this.getActivity()).get(SupportedLinkViewModel.class);
+ mSupportedLinkWrapperList = mViewModel.getSupportedLinkWrapperList();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Context context = getActivity();
+ final SupportedLinksAdapter adapter = new SupportedLinksAdapter(context,
+ mSupportedLinkWrapperList);
+ final AlertDialog.Builder builder = new AlertDialog
+ .Builder(context)
+ .setTitle(IntentPickerUtils.getCentralizedDialogTitle(getSupportedLinksTitle()))
+ .setAdapter(adapter, /* listener= */ null)
+ .setCancelable(true)
+ .setPositiveButton(R.string.app_launch_supported_links_add, (dialog, id) -> {
+ doSelectedAction();
+ })
+ .setNegativeButton(R.string.app_launch_dialog_cancel, /* listener= */ null);
+ return builder.create();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return 0;
+ }
+
+ /** Display the dialog. */
+ public void showDialog(FragmentManager manager) {
+ show(manager, DLG_ID);
+ }
+
+ private String getSupportedLinksTitle() {
+ final int supportedLinksNo = mSupportedLinkWrapperList.size();
+ return getResources().getQuantityString(
+ R.plurals.app_launch_supported_links_title, supportedLinksNo, supportedLinksNo);
+ }
+
+ private void doSelectedAction() {
+ final DomainVerificationManager manager = getActivity().getSystemService(
+ DomainVerificationManager.class);
+ final DomainVerificationUserState userState =
+ IntentPickerUtils.getDomainVerificationUserState(manager, mPackage);
+ if (userState == null || mSupportedLinkWrapperList == null) {
+ return;
+ }
+
+ updateUserSelection(manager, userState);
+ displaySelectedItem();
+ }
+
+ private void updateUserSelection(DomainVerificationManager manager,
+ DomainVerificationUserState userState) {
+ final Set<String> domainSet = new ArraySet<>();
+ for (SupportedLinkWrapper wrapper : mSupportedLinkWrapperList) {
+ if (wrapper.isChecked()) {
+ domainSet.add(wrapper.getHost());
+ }
+ }
+ if (domainSet.size() > 0) {
+ setDomainVerificationUserSelection(manager, userState.getIdentifier(),
+ domainSet, /* enabled= */true);
+ }
+ }
+
+ private void setDomainVerificationUserSelection(DomainVerificationManager manager,
+ UUID identifier, Set<String> domainSet, boolean isEnabled) {
+ try {
+ manager.setDomainVerificationUserSelection(identifier, domainSet, isEnabled);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "addSelectedItems : " + e.getMessage());
+ }
+ }
+
+ private void displaySelectedItem() {
+ final List<Fragment> fragments = getActivity().getSupportFragmentManager().getFragments();
+ for (Fragment fragment : fragments) {
+ if (fragment instanceof AppLaunchSettings) {
+ ((AppLaunchSettings) fragment).addSelectedLinksPreference();
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/intentpicker/VerifiedLinksPreference.java b/src/com/android/settings/applications/intentpicker/VerifiedLinksPreference.java
new file mode 100644
index 0000000..5452a2a
--- /dev/null
+++ b/src/com/android/settings/applications/intentpicker/VerifiedLinksPreference.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.applications.intentpicker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.CheckBox;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settingslib.widget.TwoTargetPreference;
+
+/** This customized VerifiedLinksPreference was belonged to Open by default page */
+public class VerifiedLinksPreference extends TwoTargetPreference {
+ private Context mContext;
+ private View.OnClickListener mOnWidgetClickListener;
+ private boolean mShowCheckBox;
+
+ public VerifiedLinksPreference(Context context, AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context);
+ }
+
+ public VerifiedLinksPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, /* defStyleRes= */0);
+ }
+
+ public VerifiedLinksPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public VerifiedLinksPreference(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ private void init(Context context) {
+ mContext = context;
+ mOnWidgetClickListener = null;
+ mShowCheckBox = true;
+ setLayoutResource(R.layout.preference_checkable_two_target);
+ setWidgetLayoutResource(R.layout.verified_links_widget);
+ }
+
+ /**
+ * Register a callback to be invoked when this widget is clicked.
+ *
+ * @param listener The callback that will run.
+ */
+ public void setWidgetFrameClickListener(View.OnClickListener listener) {
+ mOnWidgetClickListener = listener;
+ }
+
+ /** Determine the visibility of the {@link CheckBox}. */
+ public void setCheckBoxVisible(boolean isVisible) {
+ mShowCheckBox = isVisible;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ final View settingsWidget = view.findViewById(android.R.id.widget_frame);
+ final View divider = view.findViewById(R.id.two_target_divider);
+ divider.setVisibility(View.VISIBLE);
+ settingsWidget.setVisibility(View.VISIBLE);
+ if (mOnWidgetClickListener != null) {
+ settingsWidget.setOnClickListener(mOnWidgetClickListener);
+ }
+ final View checkboxContainer = view.findViewById(R.id.checkbox_container);
+ final View parentView = (View) checkboxContainer.getParent();
+ parentView.setEnabled(false);
+ parentView.setClickable(false);
+ CheckBox checkBox = (CheckBox) view.findViewById(com.android.internal.R.id.checkbox);
+ if (checkBox != null) {
+ checkBox.setChecked(true);
+ checkBox.setVisibility(mShowCheckBox ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index e98555b..a92f539 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -277,7 +277,7 @@
Intent intent = activity.getIntent();
Bundle args = getArguments();
int screenTitle = intent.getIntExtra(
- SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.application_info_label);
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.all_apps);
String className = args != null ? args.getString(EXTRA_CLASSNAME) : null;
if (className == null) {
className = intent.getComponent().getClassName();
@@ -342,7 +342,7 @@
screenTitle = R.string.app_notifications_title;
} else {
if (screenTitle == -1) {
- screenTitle = R.string.application_info_label;
+ screenTitle = R.string.all_apps;
}
mListType = LIST_TYPE_MAIN;
}
diff --git a/src/com/android/settings/applications/managedomainurls/DomainAppPreferenceController.java b/src/com/android/settings/applications/managedomainurls/DomainAppPreferenceController.java
index c859db2..07c4858 100644
--- a/src/com/android/settings/applications/managedomainurls/DomainAppPreferenceController.java
+++ b/src/com/android/settings/applications/managedomainurls/DomainAppPreferenceController.java
@@ -28,7 +28,7 @@
import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
-import com.android.settings.applications.AppLaunchSettings;
+import com.android.settings.applications.intentpicker.AppLaunchSettings;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
index 1fe3f17..0625bbb 100644
--- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
+++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminAdd.java
@@ -71,6 +71,7 @@
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
import org.xmlpull.v1.XmlPullParserException;
@@ -79,7 +80,10 @@
import java.util.List;
import java.util.Optional;
-public class DeviceAdminAdd extends Activity {
+/**
+ * A confirmation screen for enabling administractor.
+ */
+public class DeviceAdminAdd extends CollapsingToolbarBaseActivity {
static final String TAG = "DeviceAdminAdd";
static final int DIALOG_WARNING = 1;
diff --git a/src/com/android/settings/biometrics/BiometricUtils.java b/src/com/android/settings/biometrics/BiometricUtils.java
index d93a321..f338eb5 100644
--- a/src/com/android/settings/biometrics/BiometricUtils.java
+++ b/src/com/android/settings/biometrics/BiometricUtils.java
@@ -24,6 +24,7 @@
import android.content.IntentSender;
import android.os.storage.StorageManager;
import android.util.Log;
+import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -241,4 +242,14 @@
}
return false;
}
+
+ /**
+ * Returns {@code true} if the screen is going into a landscape mode and the angle is equal to
+ * 270.
+ * @param context Context that we use to get the display this context is associated with
+ * @return True if the angle of the rotation is equal to 270.
+ */
+ public static boolean isReverseLandscape(@NonNull Context context) {
+ return context.getDisplay().getRotation() == Surface.ROTATION_270;
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 10b31e3..8def63d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -47,6 +47,7 @@
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricErrorDialog;
+import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -98,6 +99,7 @@
private boolean mCanAssumeUdfps;
@Nullable private ProgressBar mProgressBar;
private ObjectAnimator mProgressAnim;
+ private TextView mDescriptionText;
private TextView mErrorText;
private Interpolator mFastOutSlowInInterpolator;
private Interpolator mLinearOutSlowInInterpolator;
@@ -145,7 +147,11 @@
mCanAssumeUdfps = props.size() == 1 && props.get(0).isAnyUdfpsType();
if (mCanAssumeUdfps) {
- setContentView(R.layout.udfps_enroll_enrolling);
+ if (BiometricUtils.isReverseLandscape(getApplicationContext())) {
+ setContentView(R.layout.udfps_enroll_enrolling_land);
+ } else {
+ setContentView(R.layout.udfps_enroll_enrolling);
+ }
setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
} else {
setContentView(R.layout.fingerprint_enroll_enrolling);
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
index 94eb278..8bc03ac 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollFindSensor.java
@@ -112,7 +112,11 @@
protected int getContentView() {
if (mCanAssumeUdfps) {
- return R.layout.udfps_enroll_find_sensor_layout;
+ if (BiometricUtils.isReverseLandscape(getApplicationContext())) {
+ return R.layout.udfps_enroll_find_sensor_land;
+ } else {
+ return R.layout.udfps_enroll_find_sensor_layout;
+ }
}
return R.layout.fingerprint_enroll_find_sensor;
}
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index 096c2c5..bc5aa47 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -192,6 +192,7 @@
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
BluetoothDevice.METADATA_MAIN_ICON,
BluetoothDevice.METADATA_MAIN_BATTERY,
+ BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD,
BluetoothDevice.METADATA_MAIN_CHARGING,
/* titleResId */ 0,
MAIN_DEVICE_ID);
@@ -202,6 +203,7 @@
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_left),
BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON,
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
+ BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING,
R.string.bluetooth_left_name,
LEFT_DEVICE_ID);
@@ -209,6 +211,7 @@
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
BluetoothDevice.METADATA_UNTETHERED_CASE_ICON,
BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY,
+ BluetoothDevice.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD,
BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING,
R.string.bluetooth_middle_name,
CASE_DEVICE_ID);
@@ -216,6 +219,7 @@
updateSubLayout(mLayoutPreference.findViewById(R.id.layout_right),
BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON,
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY,
+ BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING,
R.string.bluetooth_right_name,
RIGHT_DEVICE_ID);
@@ -243,7 +247,7 @@
}
private void updateSubLayout(LinearLayout linearLayout, int iconMetaKey, int batteryMetaKey,
- int chargeMetaKey, int titleResId, int deviceId) {
+ int lowBatteryMetaKey, int chargeMetaKey, int titleResId, int deviceId) {
if (linearLayout == null) {
return;
}
@@ -273,7 +277,15 @@
linearLayout.setVisibility(View.VISIBLE);
batterySummaryView.setText(com.android.settings.Utils.formatPercentage(batteryLevel));
batterySummaryView.setVisibility(View.VISIBLE);
- showBatteryIcon(linearLayout, batteryLevel, charging, batteryMetaKey);
+ int lowBatteryLevel = BluetoothUtils.getIntMetaData(bluetoothDevice, lowBatteryMetaKey);
+ if (lowBatteryLevel == BluetoothUtils.META_INT_ERROR) {
+ if (batteryMetaKey == BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY) {
+ lowBatteryLevel = CASE_LOW_BATTERY_LEVEL;
+ } else {
+ lowBatteryLevel = LOW_BATTERY_LEVEL;
+ }
+ }
+ showBatteryIcon(linearLayout, batteryLevel, lowBatteryLevel, charging);
} else {
if (deviceId == MAIN_DEVICE_ID) {
linearLayout.setVisibility(View.VISIBLE);
@@ -354,11 +366,8 @@
});
}
- private void showBatteryIcon(LinearLayout linearLayout, int level, boolean charging,
- int batteryMetaKey) {
- final int lowBatteryLevel =
- batteryMetaKey == BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY
- ? CASE_LOW_BATTERY_LEVEL : LOW_BATTERY_LEVEL;
+ private void showBatteryIcon(LinearLayout linearLayout, int level, int lowBatteryLevel,
+ boolean charging) {
final boolean enableLowBattery = level <= lowBatteryLevel && !charging;
final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon);
if (enableLowBattery) {
diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
index 27f10da..3bf9132 100644
--- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java
@@ -23,7 +23,7 @@
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.location.LocationServices;
+import com.android.settings.location.BluetoothScanningFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.utils.AnnotationSpan;
import com.android.settings.widget.SwitchWidgetController;
@@ -100,7 +100,7 @@
public void onClick(View v) {
// send users to scanning settings if they click on the link in the summary text
new SubSettingLauncher(mContext)
- .setDestination(LocationServices.class.getName())
+ .setDestination(BluetoothScanningFragment.class.getName())
.setSourceMetricsCategory(SettingsEnums.BLUETOOTH_FRAGMENT)
.launch();
}
@@ -117,4 +117,4 @@
mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
index 2891e3a..c324aca 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
@@ -20,7 +20,7 @@
import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.applications.manageapplications.ManageApplications;
-import com.android.settings.location.LocationSettings;
+import com.android.settings.location.LocationServices;
import com.android.settings.location.RecentLocationAccessSeeAllFragment;
import java.util.Map;
@@ -42,9 +42,9 @@
ProfileSelectAccountFragment.class.getName());
FRAGMENT_MAP.put(ManageApplications.class.getName(),
ProfileSelectManageApplications.class.getName());
- FRAGMENT_MAP.put(LocationSettings.class.getName(),
- ProfileSelectLocationFragment.class.getName());
FRAGMENT_MAP.put(RecentLocationAccessSeeAllFragment.class.getName(),
ProfileSelectRecentLocationAccessFragment.class.getName());
+ FRAGMENT_MAP.put(LocationServices.class.getName(),
+ ProfileSelectLocationServicesFragment.class.getName());
}
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.java
new file mode 100644
index 0000000..b6f03d6
--- /dev/null
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectLocationServicesFragment.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.dashboard.profileselector;
+
+import android.os.Bundle;
+
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.location.LocationServices;
+import com.android.settings.location.LocationServicesForWork;
+
+/**
+ * Location Services page for personal/managed profile.
+ */
+public class ProfileSelectLocationServicesFragment extends ProfileSelectFragment {
+
+ @Override
+ public Fragment[] getFragments() {
+ final Bundle workOnly = new Bundle();
+ workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
+ final Fragment workFragment = new LocationServicesForWork();
+ workFragment.setArguments(workOnly);
+
+ final Bundle personalOnly = new Bundle();
+ personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
+ final Fragment personalFragment = new LocationServices();
+ personalFragment.setArguments(personalOnly);
+ return new Fragment[]{
+ personalFragment, // 0
+ workFragment
+ };
+ }
+}
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index 6c845a9..0577e9c 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -174,8 +174,8 @@
@VisibleForTesting
List<DataUsageSummaryNode> getDensedStatsData(List<NetworkCycleData> usageSummary) {
final List<DataUsageSummaryNode> dataUsageSummaryNodes = new ArrayList<>();
- final long overallDataUsage = usageSummary.stream()
- .mapToLong(NetworkCycleData::getTotalUsage).sum();
+ final long overallDataUsage = Math.max(1L, usageSummary.stream()
+ .mapToLong(NetworkCycleData::getTotalUsage).sum());
long cumulatedDataUsage = 0L;
int cumulatedDataUsagePercentage = 0;
diff --git a/src/com/android/settings/datetime/DateTimeSettings.java b/src/com/android/settings/datetime/DateTimeSettings.java
index 469a4c7..d74847f 100644
--- a/src/com/android/settings/datetime/DateTimeSettings.java
+++ b/src/com/android/settings/datetime/DateTimeSettings.java
@@ -59,6 +59,7 @@
public void onAttach(Context context) {
super.onAttach(context);
getSettingsLifecycle().addObserver(new TimeChangeListenerMixin(context, this));
+ use(LocationTimeZoneDetectionPreferenceController.class).setFragment(this);
}
@Override
@@ -77,6 +78,7 @@
final AutoTimeFormatPreferenceController autoTimeFormatPreferenceController =
new AutoTimeFormatPreferenceController(
activity, this /* UpdateTimeAndDateCallback */);
+
controllers.add(autoTimeZonePreferenceController);
controllers.add(autoTimePreferenceController);
controllers.add(autoTimeFormatPreferenceController);
diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
index 0b0fa27..cb39635 100644
--- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
@@ -31,6 +31,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
@@ -46,17 +47,24 @@
extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnStop, TimeManager.TimeZoneDetectorListener {
+ private static final String TAG = "location_time_zone_detection";
+
private final TimeManager mTimeManager;
private final LocationManager mLocationManager;
private TimeZoneCapabilitiesAndConfig mTimeZoneCapabilitiesAndConfig;
+ private InstrumentedPreferenceFragment mFragment;
private Preference mPreference;
- public LocationTimeZoneDetectionPreferenceController(Context context, String key) {
- super(context, key);
+ public LocationTimeZoneDetectionPreferenceController(Context context) {
+ super(context, TAG);
mTimeManager = context.getSystemService(TimeManager.class);
mLocationManager = context.getSystemService(LocationManager.class);
}
+ void setFragment(InstrumentedPreferenceFragment fragment) {
+ mFragment = fragment;
+ }
+
@Override
public boolean isChecked() {
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
@@ -67,10 +75,17 @@
@Override
public boolean setChecked(boolean isChecked) {
- TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
- .setGeoDetectionEnabled(isChecked)
- .build();
- return mTimeManager.updateTimeZoneConfiguration(configuration);
+ if (isChecked && !mLocationManager.isLocationEnabled()) {
+ new LocationToggleDisabledDialogFragment(mContext)
+ .show(mFragment.getFragmentManager(), TAG);
+ // Toggle status is not updated.
+ return false;
+ } else {
+ TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
+ .setGeoDetectionEnabled(isChecked)
+ .build();
+ return mTimeManager.updateTimeZoneConfiguration(configuration);
+ }
}
@Override
diff --git a/src/com/android/settings/datetime/LocationToggleDisabledDialogFragment.java b/src/com/android/settings/datetime/LocationToggleDisabledDialogFragment.java
new file mode 100644
index 0000000..61d46c6
--- /dev/null
+++ b/src/com/android/settings/datetime/LocationToggleDisabledDialogFragment.java
@@ -0,0 +1,63 @@
+/*
+ * 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.datetime;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Dialog shown when user tries to enable GeoTZ with Location toggle disabled.
+ */
+public class LocationToggleDisabledDialogFragment extends InstrumentedDialogFragment {
+
+ private final Context mContext;
+
+ public LocationToggleDisabledDialogFragment(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.location_time_zone_detection_location_is_off_dialog_title)
+ .setIcon(R.drawable.ic_warning_24dp)
+ .setMessage(R.string.location_time_zone_detection_location_is_off_dialog_message)
+ .setPositiveButton(
+ R.string.location_time_zone_detection_location_is_off_dialog_ok_button,
+ (dialog, which) -> {
+ Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ mContext.startActivity(intent);
+ })
+ .setNegativeButton(
+ R.string.location_time_zone_detection_location_is_off_dialog_cancel_button,
+ (dialog, which) -> {})
+ .create();
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_DATE_TIME_ENABLE_GEOTZ_WITH_DISABLED_LOCATION;
+ }
+}
diff --git a/src/com/android/settings/datetime/timezone/RegionSearchPicker.java b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java
index 07986e2..85d5d70 100644
--- a/src/com/android/settings/datetime/timezone/RegionSearchPicker.java
+++ b/src/com/android/settings/datetime/timezone/RegionSearchPicker.java
@@ -71,14 +71,14 @@
final FilteredCountryTimeZones countryTimeZones = mTimeZoneData.lookupCountryTimeZones(
regionId);
final Activity activity = getActivity();
- if (countryTimeZones == null || countryTimeZones.getTimeZoneIds().isEmpty()) {
+ if (countryTimeZones == null || countryTimeZones.getPreferredTimeZoneIds().isEmpty()) {
Log.e(TAG, "Region has no time zones: " + regionId);
activity.setResult(Activity.RESULT_CANCELED);
activity.finish();
return;
}
- List<String> timeZoneIds = countryTimeZones.getTimeZoneIds();
+ List<String> timeZoneIds = countryTimeZones.getPreferredTimeZoneIds();
// Choose the time zone associated the region if there is only one time zone in that region
if (timeZoneIds.size() == 1) {
final Intent resultData = new Intent()
diff --git a/src/com/android/settings/datetime/timezone/RegionZonePicker.java b/src/com/android/settings/datetime/timezone/RegionZonePicker.java
index 8e4aa05..7f988cd 100644
--- a/src/com/android/settings/datetime/timezone/RegionZonePicker.java
+++ b/src/com/android/settings/datetime/timezone/RegionZonePicker.java
@@ -103,7 +103,7 @@
// It could be a timely operations if there are many time zones. A region in time zone data
// contains a maximum of 29 time zones currently. It may change in the future, but it's
// unlikely to be changed drastically.
- return getRegionTimeZoneInfo(filteredCountryTimeZones.getTimeZoneIds());
+ return getRegionTimeZoneInfo(filteredCountryTimeZones.getPreferredTimeZoneIds());
}
/**
diff --git a/src/com/android/settings/datetime/timezone/TimeZoneSettings.java b/src/com/android/settings/datetime/timezone/TimeZoneSettings.java
index 60cd636..6c779b5 100644
--- a/src/com/android/settings/datetime/timezone/TimeZoneSettings.java
+++ b/src/com/android/settings/datetime/timezone/TimeZoneSettings.java
@@ -214,10 +214,11 @@
mTimeZoneData.lookupCountryTimeZones(regionId);
use(RegionZonePreferenceController.class).setTimeZoneInfo(tzInfo);
- // Only clickable when the region has more than 1 time zones or no time zone is selected.
+ // Only clickable when the region has more than 1 time zones or no time zone is selected.
use(RegionZonePreferenceController.class).setClickable(tzInfo == null ||
- (countryTimeZones != null && countryTimeZones.getTimeZoneIds().size() > 1));
+ (countryTimeZones != null
+ && countryTimeZones.getPreferredTimeZoneIds().size() > 1));
use(TimeZoneInfoPreferenceController.class).setTimeZoneInfo(tzInfo);
updatePreferenceStates();
@@ -244,7 +245,8 @@
FilteredCountryTimeZones countryTimeZones =
timeZoneData.lookupCountryTimeZones(regionId);
- if (countryTimeZones == null || !countryTimeZones.getTimeZoneIds().contains(tzId)) {
+ if (countryTimeZones == null
+ || !countryTimeZones.getPreferredTimeZoneIds().contains(tzId)) {
Log.e(TAG, "Unknown time zone id is selected: " + tzId);
return;
}
diff --git a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java
index 7a38853..05a542d 100644
--- a/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java
+++ b/src/com/android/settings/datetime/timezone/model/FilteredCountryTimeZones.java
@@ -16,11 +16,15 @@
package com.android.settings.datetime.timezone.model;
+import android.util.ArraySet;
+
import com.android.i18n.timezone.CountryTimeZones;
+import java.time.Instant;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.stream.Collectors;
+import java.util.Set;
/**
* Wrap {@class CountryTimeZones} to filter time zone that are shown in the picker.
@@ -39,31 +43,46 @@
* a timestamp known to be in the recent past is used. This should be updated occasionally but
* it doesn't have to be very often.
*/
- private static final long MIN_USE_DATE_OF_TIMEZONE = 1546300800000L; // 1/1/2019 00:00 UTC
+ private static final Instant MIN_USE_DATE_OF_TIMEZONE =
+ Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC
private final CountryTimeZones mCountryTimeZones;
- private final List<String> mTimeZoneIds;
+ private final List<String> mPreferredTimeZoneIds;
+ private final Set<String> mAlternativeTimeZoneIds;
public FilteredCountryTimeZones(CountryTimeZones countryTimeZones) {
mCountryTimeZones = countryTimeZones;
- List<String> timeZoneIds = countryTimeZones.getTimeZoneMappings().stream()
- .filter(timeZoneMapping ->
- timeZoneMapping.isShownInPicker()
- && (timeZoneMapping.getNotUsedAfter() == null
- || timeZoneMapping.getNotUsedAfter() >= MIN_USE_DATE_OF_TIMEZONE))
- .map(timeZoneMapping -> timeZoneMapping.getTimeZoneId())
- .collect(Collectors.toList());
- mTimeZoneIds = Collections.unmodifiableList(timeZoneIds);
+ List<String> timeZoneIds = new ArrayList<>();
+ Set<String> alternativeTimeZoneIds = new ArraySet<>();
+ for (CountryTimeZones.TimeZoneMapping timeZoneMapping :
+ countryTimeZones.getTimeZoneMappings()) {
+ if (timeZoneMapping.isShownInPickerAt(MIN_USE_DATE_OF_TIMEZONE)) {
+ String timeZoneId = timeZoneMapping.getTimeZoneId();
+ timeZoneIds.add(timeZoneId);
+ alternativeTimeZoneIds.addAll(timeZoneMapping.getAlternativeIds());
+ }
+ }
+ mPreferredTimeZoneIds = Collections.unmodifiableList(timeZoneIds);
+ mAlternativeTimeZoneIds = Collections.unmodifiableSet(alternativeTimeZoneIds);
}
- public List<String> getTimeZoneIds() {
- return mTimeZoneIds;
+ public List<String> getPreferredTimeZoneIds() {
+ return mPreferredTimeZoneIds;
}
public CountryTimeZones getCountryTimeZones() {
return mCountryTimeZones;
}
+ /**
+ * Returns whether {@code timeZoneId} is currently used in the country or is an alternative
+ * name of a currently used time zone.
+ */
+ public boolean matches(String timeZoneId) {
+ return mPreferredTimeZoneIds.contains(timeZoneId)
+ || mAlternativeTimeZoneIds.contains(timeZoneId);
+ }
+
public String getRegionId() {
return TimeZoneData.normalizeRegionId(mCountryTimeZones.getCountryIso());
}
diff --git a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
index 335e6e2..06f2de0 100644
--- a/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
+++ b/src/com/android/settings/datetime/timezone/model/TimeZoneData.java
@@ -71,7 +71,7 @@
Set<String> regionIds = new ArraySet<>();
for (CountryTimeZones countryTimeZone : countryTimeZones) {
FilteredCountryTimeZones filteredZones = new FilteredCountryTimeZones(countryTimeZone);
- if (filteredZones.getTimeZoneIds().contains(tzId)) {
+ if (filteredZones.matches(tzId)) {
regionIds.add(filteredZones.getRegionId());
}
}
diff --git a/src/com/android/settings/development/DefaultUsbConfigurationPreferenceController.java b/src/com/android/settings/development/DefaultUsbConfigurationPreferenceController.java
index 905c552..be7704f 100644
--- a/src/com/android/settings/development/DefaultUsbConfigurationPreferenceController.java
+++ b/src/com/android/settings/development/DefaultUsbConfigurationPreferenceController.java
@@ -54,4 +54,11 @@
mPreference.setDisabledByAdmin(
checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
}
+
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ super.onDeveloperOptionsSwitchEnabled();
+ mPreference.setDisabledByAdmin(
+ checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
+ }
}
diff --git a/src/com/android/settings/development/UsbAudioRoutingPreferenceController.java b/src/com/android/settings/development/UsbAudioRoutingPreferenceController.java
index 8aa4f3c..e130b2b 100644
--- a/src/com/android/settings/development/UsbAudioRoutingPreferenceController.java
+++ b/src/com/android/settings/development/UsbAudioRoutingPreferenceController.java
@@ -83,4 +83,11 @@
Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}
+
+ @Override
+ protected void onDeveloperOptionsSwitchEnabled() {
+ super.onDeveloperOptionsSwitchEnabled();
+ mPreference.setDisabledByAdmin(
+ checkIfUsbDataSignalingIsDisabled(mContext, UserHandle.myUserId()));
+ }
}
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..03a7b97 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,233 @@
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()) {
+ // Set null volume to hide category stats.
+ mPreferenceController.setVolume(null);
+ return;
+ }
+ if (mSelectedStorageEntry.isPrivate()) {
+ // Stats data is only available on private volumes.
+ 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.setVolume(mSelectedStorageEntry.getVolumeInfo());
+ }
+ }
@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 +303,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 +343,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 +382,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 +431,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 +443,7 @@
@VisibleForTesting
protected void setVolume(VolumeInfo info) {
- mVolume = info;
+ mSelectedStorageEntry = new StorageEntry(getContext(), info);
}
/**
@@ -260,7 +494,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 +511,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 +589,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 +628,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/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 0766a74..65ef6f4 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -35,6 +35,7 @@
private ProgressBar mProgressBar;
private static final int PROGRESS_MAX = 100;
private int mProgressPercent = UNINITIALIZED;
+ private long mStorageSize;
public StorageItemPreference(Context context) {
this(context, null);
@@ -47,6 +48,7 @@
}
public void setStorageSize(long size, long total) {
+ mStorageSize = size;
setSummary(
FileSizeFormatter.formatFileSize(
getContext(),
@@ -61,6 +63,10 @@
updateProgressBar();
}
+ public long getStorageSize() {
+ return mStorageSize;
+ }
+
protected void updateProgressBar() {
if (mProgressBar == null || mProgressPercent == UNINITIALIZED)
return;
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/storage/AutomaticStorageManagementSwitchPreferenceController.java b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
index f733c72..f753868 100644
--- a/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceController.java
@@ -26,6 +26,7 @@
import androidx.fragment.app.FragmentManager;
import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.deletionhelper.ActivationWarningFragment;
import com.android.settings.overlay.FeatureFactory;
@@ -66,6 +67,9 @@
@Override
public int getAvailabilityStatus() {
+ if (!mContext.getResources().getBoolean(R.bool.config_show_smart_storage_toggle)) {
+ return UNSUPPORTED_ON_DEVICE;
+ }
return !ActivityManager.isLowRamDeviceStatic() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@@ -108,4 +112,4 @@
return true;
}
-}
\ No newline at end of file
+}
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/EmptyTrashFragment.java b/src/com/android/settings/deviceinfo/storage/EmptyTrashFragment.java
new file mode 100644
index 0000000..da7b9ba
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/EmptyTrashFragment.java
@@ -0,0 +1,57 @@
+/*
+ * 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.os.Bundle;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Dialog asks if users want to empty trash files.
+ */
+public class EmptyTrashFragment extends InstrumentedDialogFragment {
+ private static final String TAG_EMPTY_TRASH = "empty_trash";
+
+ /** Shows the empty trash dialog. */
+ public static void show(Fragment parent) {
+ final EmptyTrashFragment dialog = new EmptyTrashFragment();
+ dialog.setTargetFragment(parent, 0 /* requestCode */);
+ dialog.show(parent.getFragmentManager(), TAG_EMPTY_TRASH);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DIALOG_EMPTY_TRASH;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ return builder.setTitle(R.string.storage_trash_dialog_title)
+ .setMessage(R.string.storage_trash_dialog_ask_message)
+ .setPositiveButton(R.string.storage_trash_dialog_confirm, (dialog, which) -> {
+ // TODO(170918505): Implement the logic in worker thread.
+ }).setNegativeButton(android.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..9ea0292
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageEntry.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 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;
+ }
+
+ /**
+ * Stub volume is a volume that is maintained by external party such as the ChromeOS processes
+ * in ARC++.
+ */
+ public boolean isStub() {
+ return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_STUB;
+ }
+
+ /** 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..e5259f9 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -19,12 +19,13 @@
import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.PERSONAL_TAB;
import static com.android.settings.dashboard.profileselector.ProfileSelectFragment.WORK_TAB;
-import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.TrafficStats;
+import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
@@ -53,6 +54,8 @@
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
@@ -67,20 +70,33 @@
private static final String SYSTEM_FRAGMENT_TAG = "SystemInfo";
@VisibleForTesting
- static final String PHOTO_KEY = "pref_photos_videos";
+ static final String PUBLIC_STORAGE_KEY = "pref_public_storage";
@VisibleForTesting
- static final String AUDIO_KEY = "pref_music_audio";
+ static final String IMAGES_KEY = "pref_images";
@VisibleForTesting
- static final String GAME_KEY = "pref_games";
+ static final String VIDEOS_KEY = "pref_videos";
@VisibleForTesting
- static final String MOVIES_KEY = "pref_movies";
+ static final String AUDIOS_KEY = "pref_audios";
@VisibleForTesting
- static final String OTHER_APPS_KEY = "pref_other_apps";
+ static final String APPS_KEY = "pref_apps";
+ @VisibleForTesting
+ static final String GAMES_KEY = "pref_games";
+ @VisibleForTesting
+ static final String DOCUMENTS_AND_OTHER_KEY = "pref_documents_and_other";
@VisibleForTesting
static final String SYSTEM_KEY = "pref_system";
@VisibleForTesting
- static final String FILES_KEY = "pref_files";
+ static final String TRASH_KEY = "pref_trash";
+ private final Uri mImagesUri;
+ private final Uri mVideosUri;
+ private final Uri mAudiosUri;
+ private final Uri mDocumentsAndOtherUri;
+
+ // This value should align with the design of storage_dashboard_fragment.xml
+ private static final int LAST_STORAGE_CATEGORY_PREFERENCE_ORDER = 200;
+
+ private PackageManager mPackageManager;
private final Fragment mFragment;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final StorageVolumeProvider mSvp;
@@ -89,14 +105,27 @@
private long mUsedBytes;
private long mTotalSize;
+ private List<StorageItemPreference> mPrivateStorageItemPreferences;
private PreferenceScreen mScreen;
- private StorageItemPreference mPhotoPreference;
- private StorageItemPreference mAudioPreference;
- private StorageItemPreference mGamePreference;
- private StorageItemPreference mMoviesPreference;
- private StorageItemPreference mAppPreference;
- private StorageItemPreference mFilePreference;
- private StorageItemPreference mSystemPreference;
+ @VisibleForTesting
+ Preference mPublicStoragePreference;
+ @VisibleForTesting
+ StorageItemPreference mImagesPreference;
+ @VisibleForTesting
+ StorageItemPreference mVideosPreference;
+ @VisibleForTesting
+ StorageItemPreference mAudiosPreference;
+ @VisibleForTesting
+ StorageItemPreference mAppsPreference;
+ @VisibleForTesting
+ StorageItemPreference mGamesPreference;
+ @VisibleForTesting
+ StorageItemPreference mDocumentsAndOtherPreference;
+ @VisibleForTesting
+ StorageItemPreference mSystemPreference;
+ @VisibleForTesting
+ StorageItemPreference mTrashPreference;
+
private boolean mIsWorkProfile;
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
@@ -104,11 +133,21 @@
public StorageItemPreferenceController(
Context context, Fragment hostFragment, VolumeInfo volume, StorageVolumeProvider svp) {
super(context);
+ mPackageManager = context.getPackageManager();
mFragment = hostFragment;
mVolume = volume;
mSvp = svp;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mUserId = UserHandle.myUserId();
+
+ mImagesUri = Uri.parse(context.getResources()
+ .getString(R.string.config_images_storage_category_uri));
+ mVideosUri = Uri.parse(context.getResources()
+ .getString(R.string.config_videos_storage_category_uri));
+ mAudiosUri = Uri.parse(context.getResources()
+ .getString(R.string.config_audios_storage_category_uri));
+ mDocumentsAndOtherUri = Uri.parse(context.getResources()
+ .getString(R.string.config_documents_and_other_storage_category_uri));
}
public StorageItemPreferenceController(
@@ -128,54 +167,42 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
- if (preference == null) {
- return false;
- }
-
- Intent intent = null;
if (preference.getKey() == null) {
return false;
}
switch (preference.getKey()) {
- case PHOTO_KEY:
- intent = getPhotosIntent();
- break;
- case AUDIO_KEY:
- intent = getAudioIntent();
- break;
- case GAME_KEY:
- intent = getGamesIntent();
- break;
- case MOVIES_KEY:
- intent = getMoviesIntent();
- break;
- case OTHER_APPS_KEY:
- // Because we are likely constructed with a null volume, this is theoretically
- // possible.
- if (mVolume == null) {
- break;
- }
- intent = getAppsIntent();
- break;
- case FILES_KEY:
- intent = getFilesIntent();
- FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(
- mContext, SettingsEnums.STORAGE_FILES);
- break;
+ case PUBLIC_STORAGE_KEY:
+ launchPublicStorageIntent();
+ return true;
+ case IMAGES_KEY:
+ launchImagesIntent();
+ return true;
+ case VIDEOS_KEY:
+ launchVideosIntent();
+ return true;
+ case AUDIOS_KEY:
+ launchAudiosIntent();
+ return true;
+ case APPS_KEY:
+ launchAppsIntent();
+ return true;
+ case GAMES_KEY:
+ launchGamesIntent();
+ return true;
+ case DOCUMENTS_AND_OTHER_KEY:
+ launchDocumentsAndOtherIntent();
+ return true;
case SYSTEM_KEY:
final SystemInfoFragment dialog = new SystemInfoFragment();
dialog.setTargetFragment(mFragment, 0);
dialog.show(mFragment.getFragmentManager(), SYSTEM_FRAGMENT_TAG);
return true;
+ case TRASH_KEY:
+ launchTrashIntent();
+ return true;
+ default:
+ // Do nothing.
}
-
- if (intent != null) {
- intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
-
- Utils.launchIntent(mFragment, intent);
- return true;
- }
-
return super.handlePreferenceTreeClick(preference);
}
@@ -189,37 +216,106 @@
*/
public void setVolume(VolumeInfo volume) {
mVolume = volume;
- setFilesPreferenceVisibility();
+
+ updateCategoryPreferencesVisibility();
+ updatePrivateStorageCategoryPreferencesOrder();
}
- private void setFilesPreferenceVisibility() {
- if (mScreen != null) {
+ // Stats data is only available on private volumes.
+ private boolean isValidPrivateVolume() {
+ return mVolume != null
+ && mVolume.getType() == VolumeInfo.TYPE_PRIVATE
+ && (mVolume.getState() == VolumeInfo.STATE_MOUNTED
+ || mVolume.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
+ }
+
+ private boolean isValidPublicVolume() {
+ return mVolume != null
+ && (mVolume.getType() == VolumeInfo.TYPE_PUBLIC
+ || mVolume.getType() == VolumeInfo.TYPE_STUB)
+ && (mVolume.getState() == VolumeInfo.STATE_MOUNTED
+ || mVolume.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
+ }
+
+ private void updateCategoryPreferencesVisibility() {
+ if (mScreen == null) {
+ return;
+ }
+
+ mPublicStoragePreference.setVisible(isValidPublicVolume());
+
+ final boolean privateStoragePreferencesVisible = isValidPrivateVolume();
+ mImagesPreference.setVisible(privateStoragePreferencesVisible);
+ mVideosPreference.setVisible(privateStoragePreferencesVisible);
+ mAudiosPreference.setVisible(privateStoragePreferencesVisible);
+ mAppsPreference.setVisible(privateStoragePreferencesVisible);
+ mGamesPreference.setVisible(privateStoragePreferencesVisible);
+ mDocumentsAndOtherPreference.setVisible(privateStoragePreferencesVisible);
+ mSystemPreference.setVisible(privateStoragePreferencesVisible);
+ // TODO(b/170918505): Shows trash category after trash category feature complete.
+ mTrashPreference.setVisible(false);
+
+ if (privateStoragePreferencesVisible) {
final VolumeInfo sharedVolume = mSvp.findEmulatedForPrivate(mVolume);
// If we don't have a shared volume for our internal storage (or the shared volume isn't
// mounted as readable for whatever reason), we should hide the File preference.
- final boolean hideFilePreference =
- (sharedVolume == null) || !sharedVolume.isMountedReadable();
- if (hideFilePreference) {
- mScreen.removePreference(mFilePreference);
- } else {
- mScreen.addPreference(mFilePreference);
+ if (sharedVolume == null || !sharedVolume.isMountedReadable()) {
+ mDocumentsAndOtherPreference.setVisible(false);
}
}
}
+ private void updatePrivateStorageCategoryPreferencesOrder() {
+ if (mScreen == null || !isValidPrivateVolume()) {
+ return;
+ }
+
+ if (mPrivateStorageItemPreferences == null) {
+ mPrivateStorageItemPreferences = new ArrayList<>();
+
+ mPrivateStorageItemPreferences.add(mImagesPreference);
+ mPrivateStorageItemPreferences.add(mVideosPreference);
+ mPrivateStorageItemPreferences.add(mAudiosPreference);
+ mPrivateStorageItemPreferences.add(mAppsPreference);
+ mPrivateStorageItemPreferences.add(mGamesPreference);
+ mPrivateStorageItemPreferences.add(mDocumentsAndOtherPreference);
+ mPrivateStorageItemPreferences.add(mSystemPreference);
+ mPrivateStorageItemPreferences.add(mTrashPreference);
+ }
+ mScreen.removePreference(mImagesPreference);
+ mScreen.removePreference(mVideosPreference);
+ mScreen.removePreference(mAudiosPreference);
+ mScreen.removePreference(mAppsPreference);
+ mScreen.removePreference(mGamesPreference);
+ mScreen.removePreference(mDocumentsAndOtherPreference);
+ mScreen.removePreference(mSystemPreference);
+ mScreen.removePreference(mTrashPreference);
+
+ // Sort display order by size.
+ Collections.sort(mPrivateStorageItemPreferences,
+ Comparator.comparingLong(StorageItemPreference::getStorageSize));
+ int orderIndex = LAST_STORAGE_CATEGORY_PREFERENCE_ORDER;
+ for (StorageItemPreference preference : mPrivateStorageItemPreferences) {
+ preference.setOrder(orderIndex--);
+ mScreen.addPreference(preference);
+ }
+ }
+
/**
* Sets the user id for which this preference controller is handling.
*/
public void setUserId(UserHandle userHandle) {
mUserId = userHandle.getIdentifier();
- tintPreference(mPhotoPreference);
- tintPreference(mMoviesPreference);
- tintPreference(mAudioPreference);
- tintPreference(mGamePreference);
- tintPreference(mAppPreference);
+ tintPreference(mPublicStoragePreference);
+ tintPreference(mImagesPreference);
+ tintPreference(mVideosPreference);
+ tintPreference(mAudiosPreference);
+ tintPreference(mAppsPreference);
+ tintPreference(mGamesPreference);
+ tintPreference(mDocumentsAndOtherPreference);
tintPreference(mSystemPreference);
- tintPreference(mFilePreference);
+ tintPreference(mTrashPreference);
}
private void tintPreference(Preference preference) {
@@ -240,15 +336,18 @@
@Override
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
- mPhotoPreference = screen.findPreference(PHOTO_KEY);
- mAudioPreference = screen.findPreference(AUDIO_KEY);
- mGamePreference = screen.findPreference(GAME_KEY);
- mMoviesPreference = screen.findPreference(MOVIES_KEY);
- mAppPreference = screen.findPreference(OTHER_APPS_KEY);
+ mPublicStoragePreference = screen.findPreference(PUBLIC_STORAGE_KEY);
+ mImagesPreference = screen.findPreference(IMAGES_KEY);
+ mVideosPreference = screen.findPreference(VIDEOS_KEY);
+ mAudiosPreference = screen.findPreference(AUDIOS_KEY);
+ mAppsPreference = screen.findPreference(APPS_KEY);
+ mGamesPreference = screen.findPreference(GAMES_KEY);
+ mDocumentsAndOtherPreference = screen.findPreference(DOCUMENTS_AND_OTHER_KEY);
mSystemPreference = screen.findPreference(SYSTEM_KEY);
- mFilePreference = screen.findPreference(FILES_KEY);
+ mTrashPreference = screen.findPreference(TRASH_KEY);
- setFilesPreferenceVisibility();
+ updateCategoryPreferencesVisibility();
+ updatePrivateStorageCategoryPreferencesOrder();
}
public void onLoadFinished(SparseArray<StorageAsyncLoader.AppsStorageResult> result,
@@ -257,12 +356,14 @@
final StorageAsyncLoader.AppsStorageResult profileData = result.get(
Utils.getManagedProfileId(mContext.getSystemService(UserManager.class), userId));
- mPhotoPreference.setStorageSize(getPhotosSize(data, profileData), mTotalSize);
- mAudioPreference.setStorageSize(getAudioSize(data, profileData), mTotalSize);
- mGamePreference.setStorageSize(getGamesSize(data, profileData), mTotalSize);
- mMoviesPreference.setStorageSize(getMoviesSize(data, profileData), mTotalSize);
- mAppPreference.setStorageSize(getAppsSize(data, profileData), mTotalSize);
- mFilePreference.setStorageSize(getFilesSize(data, profileData), mTotalSize);
+ mImagesPreference.setStorageSize(getImagesSize(data, profileData), mTotalSize);
+ mVideosPreference.setStorageSize(getVideosSize(data, profileData), mTotalSize);
+ mAudiosPreference.setStorageSize(getAudiosSize(data, profileData), mTotalSize);
+ mAppsPreference.setStorageSize(getAppsSize(data, profileData), mTotalSize);
+ mGamesPreference.setStorageSize(getGamesSize(data, profileData), mTotalSize);
+ mDocumentsAndOtherPreference.setStorageSize(getDocumentsAndOtherSize(data, profileData),
+ mTotalSize);
+ mTrashPreference.setStorageSize(getTrashSize(data, profileData), mTotalSize);
if (mSystemPreference != null) {
// Everything else that hasn't already been attributed is tracked as
@@ -283,6 +384,8 @@
final long systemSize = Math.max(TrafficStats.GB_IN_BYTES, mUsedBytes - attributedSize);
mSystemPreference.setStorageSize(systemSize, mTotalSize);
}
+
+ updatePrivateStorageCategoryPreferencesOrder();
}
public void setUsedSize(long usedSizeBytes) {
@@ -293,37 +396,39 @@
mTotalSize = totalSizeBytes;
}
- /**
- * Returns a list of keys used by this preference controller.
- */
- public static List<String> getUsedKeys() {
- List<String> list = new ArrayList<>();
- list.add(PHOTO_KEY);
- list.add(AUDIO_KEY);
- list.add(GAME_KEY);
- list.add(MOVIES_KEY);
- list.add(OTHER_APPS_KEY);
- list.add(SYSTEM_KEY);
- list.add(FILES_KEY);
- return list;
+ private void launchPublicStorageIntent() {
+ final Intent intent = mVolume.buildBrowseIntent();
+ if (intent != null) {
+ mContext.startActivity(intent);
+ }
}
- private Intent getPhotosIntent() {
- Bundle args = getWorkAnnotatedBundle(2);
- args.putString(
- ManageApplications.EXTRA_CLASSNAME, Settings.PhotosStorageActivity.class.getName());
- args.putInt(
- ManageApplications.EXTRA_STORAGE_TYPE,
- ManageApplications.STORAGE_TYPE_PHOTOS_VIDEOS);
- return new SubSettingLauncher(mContext)
- .setDestination(ManageApplications.class.getName())
- .setTitleRes(R.string.storage_photos_videos)
- .setArguments(args)
- .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
- .toIntent();
+ // TODO(b/183078080): To simplify StorageItemPreferenceController, move launchxxxIntent to a
+ // utility object.
+ private void launchImagesIntent() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(mImagesUri);
+
+ if (intent.resolveActivity(mPackageManager) == null) {
+ final Bundle args = getWorkAnnotatedBundle(2);
+ args.putString(ManageApplications.EXTRA_CLASSNAME,
+ Settings.PhotosStorageActivity.class.getName());
+ args.putInt(ManageApplications.EXTRA_STORAGE_TYPE,
+ ManageApplications.STORAGE_TYPE_PHOTOS_VIDEOS);
+ intent = new SubSettingLauncher(mContext)
+ .setDestination(ManageApplications.class.getName())
+ .setTitleRes(R.string.storage_photos_videos)
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
+ .toIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
+ } else {
+ mContext.startActivity(intent);
+ }
}
- private long getPhotosSize(StorageAsyncLoader.AppsStorageResult data,
+ private long getImagesSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.photosAppsSize + data.externalStats.imageBytes
@@ -336,26 +441,62 @@
}
}
- private Intent getAudioIntent() {
- if (mVolume == null) {
- return null;
- }
+ private void launchVideosIntent() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(mVideosUri);
- Bundle args = getWorkAnnotatedBundle(4);
- args.putString(ManageApplications.EXTRA_CLASSNAME,
- Settings.StorageUseActivity.class.getName());
- args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
- args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
- args.putInt(ManageApplications.EXTRA_STORAGE_TYPE, ManageApplications.STORAGE_TYPE_MUSIC);
- return new SubSettingLauncher(mContext)
- .setDestination(ManageApplications.class.getName())
- .setTitleRes(R.string.storage_music_audio)
- .setArguments(args)
- .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
- .toIntent();
+ if (intent.resolveActivity(mPackageManager) == null) {
+ final Bundle args = getWorkAnnotatedBundle(1);
+ args.putString(ManageApplications.EXTRA_CLASSNAME,
+ Settings.MoviesStorageActivity.class.getName());
+ intent = new SubSettingLauncher(mContext)
+ .setDestination(ManageApplications.class.getName())
+ .setTitleRes(R.string.storage_movies_tv)
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
+ .toIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
+ } else {
+ mContext.startActivity(intent);
+ }
}
- private long getAudioSize(StorageAsyncLoader.AppsStorageResult data,
+ private long getVideosSize(StorageAsyncLoader.AppsStorageResult data,
+ StorageAsyncLoader.AppsStorageResult profileData) {
+ if (profileData != null) {
+ return data.videoAppsSize + profileData.videoAppsSize;
+ } else {
+ return data.videoAppsSize;
+ }
+ }
+
+ private void launchAudiosIntent() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(mAudiosUri);
+
+ if (intent.resolveActivity(mPackageManager) == null) {
+ final Bundle args = getWorkAnnotatedBundle(4);
+ args.putString(ManageApplications.EXTRA_CLASSNAME,
+ Settings.StorageUseActivity.class.getName());
+ args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
+ args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
+ args.putInt(ManageApplications.EXTRA_STORAGE_TYPE,
+ ManageApplications.STORAGE_TYPE_MUSIC);
+ intent = new SubSettingLauncher(mContext)
+ .setDestination(ManageApplications.class.getName())
+ .setTitleRes(R.string.storage_music_audio)
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
+ .toIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
+ } else {
+ mContext.startActivity(intent);
+ }
+ }
+
+ private long getAudiosSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.musicAppsSize + data.externalStats.audioBytes
@@ -365,21 +506,20 @@
}
}
- private Intent getAppsIntent() {
- if (mVolume == null) {
- return null;
- }
+ private void launchAppsIntent() {
final Bundle args = getWorkAnnotatedBundle(3);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.StorageUseActivity.class.getName());
args.putString(ManageApplications.EXTRA_VOLUME_UUID, mVolume.getFsUuid());
args.putString(ManageApplications.EXTRA_VOLUME_NAME, mVolume.getDescription());
- return new SubSettingLauncher(mContext)
+ final Intent intent = new SubSettingLauncher(mContext)
.setDestination(ManageApplications.class.getName())
.setTitleRes(R.string.apps_storage)
.setArguments(args)
.setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
.toIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
}
private long getAppsSize(StorageAsyncLoader.AppsStorageResult data,
@@ -391,16 +531,18 @@
}
}
- private Intent getGamesIntent() {
+ private void launchGamesIntent() {
final Bundle args = getWorkAnnotatedBundle(1);
args.putString(ManageApplications.EXTRA_CLASSNAME,
Settings.GamesStorageActivity.class.getName());
- return new SubSettingLauncher(mContext)
+ final Intent intent = new SubSettingLauncher(mContext)
.setDestination(ManageApplications.class.getName())
.setTitleRes(R.string.game_storage_settings)
.setArguments(args)
.setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
.toIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
}
private long getGamesSize(StorageAsyncLoader.AppsStorageResult data,
@@ -412,27 +554,6 @@
}
}
- private Intent getMoviesIntent() {
- final Bundle args = getWorkAnnotatedBundle(1);
- args.putString(ManageApplications.EXTRA_CLASSNAME,
- Settings.MoviesStorageActivity.class.getName());
- return new SubSettingLauncher(mContext)
- .setDestination(ManageApplications.class.getName())
- .setTitleRes(R.string.storage_movies_tv)
- .setArguments(args)
- .setSourceMetricsCategory(mMetricsFeatureProvider.getMetricsCategory(mFragment))
- .toIntent();
- }
-
- private long getMoviesSize(StorageAsyncLoader.AppsStorageResult data,
- StorageAsyncLoader.AppsStorageResult profileData) {
- if (profileData != null) {
- return data.videoAppsSize + profileData.videoAppsSize;
- } else {
- return data.videoAppsSize;
- }
- }
-
private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
final Bundle args = new Bundle(1 + additionalCapacity);
args.putInt(SettingsActivity.EXTRA_SHOW_FRAGMENT_TAB,
@@ -440,11 +561,20 @@
return args;
}
- private Intent getFilesIntent() {
- return mSvp.findEmulatedForPrivate(mVolume).buildBrowseIntent();
+ private void launchDocumentsAndOtherIntent() {
+ Intent intent = new Intent(Intent.ACTION_VIEW);
+ intent.setData(mDocumentsAndOtherUri);
+
+ if (intent.resolveActivity(mPackageManager) == null) {
+ intent = mSvp.findEmulatedForPrivate(mVolume).buildBrowseIntent();
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ Utils.launchIntent(mFragment, intent);
+ } else {
+ mContext.startActivity(intent);
+ }
}
- private long getFilesSize(StorageAsyncLoader.AppsStorageResult data,
+ private long getDocumentsAndOtherSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.externalStats.totalBytes
@@ -466,6 +596,22 @@
}
}
+ private void launchTrashIntent() {
+ final Intent intent = new Intent("android.settings.VIEW_TRASH");
+
+ if (intent.resolveActivity(mPackageManager) == null) {
+ EmptyTrashFragment.show(mFragment);
+ } else {
+ mContext.startActivity(intent);
+ }
+ }
+
+ private long getTrashSize(StorageAsyncLoader.AppsStorageResult data,
+ StorageAsyncLoader.AppsStorageResult profileData) {
+ // TODO(170918505): Implement it.
+ return 0L;
+ }
+
private static long totalValues(StorageMeasurement.MeasurementDetails details, int userId,
String... keys) {
long total = 0;
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/display/AdaptiveSleepPreferenceController.java b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
index e0527f9..c16b95c 100644
--- a/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
+++ b/src/com/android/settings/display/AdaptiveSleepPreferenceController.java
@@ -60,7 +60,6 @@
mPreference = new RestrictedSwitchPreference(context);
mPreference.setTitle(R.string.adaptive_sleep_title);
mPreference.setSummary(R.string.adaptive_sleep_description);
- mPreference.setIcon(R.drawable.empty_icon);
mPreference.setChecked(isChecked());
mPreference.setKey(PREFERENCE_KEY);
mPreference.setOnPreferenceClickListener(preference -> {
diff --git a/src/com/android/settings/display/ScreenTimeoutSettings.java b/src/com/android/settings/display/ScreenTimeoutSettings.java
index 3b0d333..a15a7a0 100644
--- a/src/com/android/settings/display/ScreenTimeoutSettings.java
+++ b/src/com/android/settings/display/ScreenTimeoutSettings.java
@@ -164,7 +164,8 @@
final String textMoreDetails = getResources().getString(R.string.admin_more_details);
final SpannableString spannableString = new SpannableString(
- textDisabledByAdmin + System.lineSeparator() + textMoreDetails);
+ textDisabledByAdmin + System.lineSeparator()
+ + System.lineSeparator() + textMoreDetails);
final ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
@@ -174,7 +175,7 @@
if (textDisabledByAdmin != null && textMoreDetails != null) {
spannableString.setSpan(clickableSpan, textDisabledByAdmin.length() + 1,
- textDisabledByAdmin.length() + textMoreDetails.length() + 1,
+ textDisabledByAdmin.length() + textMoreDetails.length() + 2,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
diff --git a/src/com/android/settings/emergency/MoreSettingsPreferenceController.java b/src/com/android/settings/emergency/MoreSettingsPreferenceController.java
new file mode 100644
index 0000000..34c11c7
--- /dev/null
+++ b/src/com/android/settings/emergency/MoreSettingsPreferenceController.java
@@ -0,0 +1,117 @@
+/*
+ * 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.emergency;
+
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.util.List;
+
+/**
+ * Preference controller for More settings button
+ */
+public class MoreSettingsPreferenceController extends BasePreferenceController implements
+ View.OnClickListener {
+
+ private static final String EXTRA_KEY_ATTRIBUTION = "attribution";
+ private static final String TAG = "MoreSettingsPrefCtrl";
+ @VisibleForTesting
+ Intent mIntent;
+
+ public MoreSettingsPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ final String packageName = mContext.getResources().getString(
+ R.string.config_emergency_package_name);
+
+ if (TextUtils.isEmpty(packageName)) {
+ return;
+ }
+ mIntent = new Intent(Intent.ACTION_MAIN)
+ .setPackage(packageName);
+
+ final List<ResolveInfo> info = mContext.getPackageManager()
+ .queryIntentActivities(mIntent, MATCH_SYSTEM_ONLY);
+
+ if (info != null && !info.isEmpty()) {
+ mIntent.setClassName(packageName, info.get(0).activityInfo.name);
+ } else {
+ mIntent = null;
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final LayoutPreference pref = screen.findPreference(getPreferenceKey());
+ final Button button = pref.findViewById(R.id.button);
+ button.setText(getButtonText());
+ button.setOnClickListener(this);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mIntent == null) {
+ return UNSUPPORTED_ON_DEVICE;
+ } else {
+ return AVAILABLE;
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ final Intent intent = new Intent(mIntent)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_KEY_ATTRIBUTION, mContext.getPackageName());
+ mContext.startActivity(intent, bundle);
+ }
+
+ private CharSequence getButtonText() {
+ final String packageName = mContext.getResources().getString(
+ R.string.config_emergency_package_name);
+ try {
+ final PackageManager pm = mContext.getPackageManager();
+ final ApplicationInfo appInfo = pm.getApplicationInfo(
+ packageName, MATCH_DISABLED_COMPONENTS
+ | MATCH_DISABLED_UNTIL_USED_COMPONENTS);
+ return mContext.getString(R.string.open_app_button, appInfo.loadLabel(pm));
+ } catch (Exception e) {
+ Log.d(TAG, "Failed to get open app button text, falling back.");
+ return "";
+ }
+ }
+}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
index b07eb91..8353d20 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceController.java
@@ -13,6 +13,9 @@
*/
package com.android.settings.enterprise;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import androidx.preference.Preference;
@@ -39,6 +42,10 @@
if (preference == null) {
return;
}
+ if (isFinancedDevice()) {
+ preference.setTitle(R.string.financed_privacy_settings);
+ }
+
final String organizationName = mFeatureProvider.getDeviceOwnerOrganizationName();
if (organizationName == null) {
preference.setSummary(R.string.enterprise_privacy_settings_summary_generic);
@@ -57,4 +64,10 @@
public String getPreferenceKey() {
return KEY_ENTERPRISE_PRIVACY;
}
+
+ private boolean isFinancedDevice() {
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.isDeviceManaged() && dpm.getDeviceOwnerType(
+ dpm.getDeviceOwnerComponentOnAnyUser()) == DEVICE_OWNER_TYPE_FINANCED;
+ }
}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index dd0a9ce..1aad544 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -20,16 +20,13 @@
import android.content.Context;
import android.provider.SearchIndexableResource;
-import com.android.settings.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
@SearchIndexable
@@ -37,6 +34,23 @@
static final String TAG = "EnterprisePrivacySettings";
+ @VisibleForTesting
+ PrivacySettingsPreference mPrivacySettingsPreference;
+
+ @Override
+ public void onAttach(Context context) {
+ mPrivacySettingsPreference =
+ PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(context);
+
+ super.onAttach(context);
+ }
+
+ @Override
+ public void onDetach() {
+ mPrivacySettingsPreference = null;
+ super.onDetach();
+ }
+
@Override
public int getMetricsCategory() {
return SettingsEnums.ENTERPRISE_PRIVACY_SETTINGS;
@@ -49,47 +63,12 @@
@Override
protected int getPreferenceScreenResId() {
- return R.xml.enterprise_privacy_settings;
+ return mPrivacySettingsPreference.getPreferenceScreenResId();
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
- return buildPreferenceControllers(context, true /* async */);
- }
-
- private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
- boolean async) {
- final List<AbstractPreferenceController> controllers = new ArrayList<>();
- controllers.add(new NetworkLogsPreferenceController(context));
- controllers.add(new BugReportsPreferenceController(context));
- controllers.add(new SecurityLogsPreferenceController(context));
- final List<AbstractPreferenceController> exposureChangesCategoryControllers =
- new ArrayList<>();
- exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
- context, async));
- exposureChangesCategoryControllers.add(
- new AdminGrantedLocationPermissionsPreferenceController(context, async));
- exposureChangesCategoryControllers.add(
- new AdminGrantedMicrophonePermissionPreferenceController(context, async));
- exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
- context, async));
- exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
- context));
- exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
- context));
- exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
- context));
- exposureChangesCategoryControllers.add(new ImePreferenceController(context));
- exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(context));
- exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(context));
- exposureChangesCategoryControllers.add(new CaCertsManagedProfilePreferenceController(
- context));
- controllers.addAll(exposureChangesCategoryControllers);
- controllers.add(new PreferenceCategoryController(context, "exposure_changes_category")
- .setChildren(exposureChangesCategoryControllers));
- controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(context));
- controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(context));
- return controllers;
+ return mPrivacySettingsPreference.createPreferenceControllers(true /* async */);
}
public static boolean isPageEnabled(Context context) {
@@ -99,17 +78,32 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.enterprise_privacy_settings) {
+ new BaseSearchIndexProvider() {
+
+ private PrivacySettingsPreference mPrivacySettingsPreference;
+
@Override
protected boolean isPageSearchEnabled(Context context) {
return isPageEnabled(context);
}
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+ boolean enabled) {
+ mPrivacySettingsPreference =
+ PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(
+ context);
+ return mPrivacySettingsPreference.getXmlResourcesToIndex();
+ }
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
- return buildPreferenceControllers(context, false /* async */);
+ mPrivacySettingsPreference =
+ PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(
+ context);
+ return mPrivacySettingsPreference.createPreferenceControllers(
+ false /* async */);
}
};
}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java b/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java
new file mode 100644
index 0000000..19556a1
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java
@@ -0,0 +1,97 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Privacy Settings preferences for an Enterprise device. */
+public class PrivacySettingsEnterprisePreference implements PrivacySettingsPreference {
+ private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+
+ private final Context mContext;
+
+ public PrivacySettingsEnterprisePreference(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ /**
+ * Returns the XML Res Id that is used for an Enterprise device in the Privacy Settings screen.
+ */
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.enterprise_privacy_settings;
+ }
+
+ /**
+ * Returns the Enterprise XML resources to index for an Enterprise device.
+ */
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex() {
+ final SearchIndexableResource sir = new SearchIndexableResource(mContext);
+ sir.xmlResId = getPreferenceScreenResId();
+ return Collections.singletonList(sir);
+ }
+
+ /**
+ * Returns the preference controllers used to populate the privacy preferences in the Privacy
+ * Settings screen for Enterprise devices.
+ */
+ @Override
+ public List<AbstractPreferenceController> createPreferenceControllers(boolean async) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new NetworkLogsPreferenceController(mContext));
+ controllers.add(new BugReportsPreferenceController(mContext));
+ controllers.add(new SecurityLogsPreferenceController(mContext));
+ final List<AbstractPreferenceController> exposureChangesCategoryControllers =
+ new ArrayList<>();
+ exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
+ mContext, async));
+ exposureChangesCategoryControllers.add(
+ new AdminGrantedLocationPermissionsPreferenceController(mContext, async));
+ exposureChangesCategoryControllers.add(
+ new AdminGrantedMicrophonePermissionPreferenceController(mContext, async));
+ exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
+ mContext, async));
+ exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
+ mContext));
+ exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
+ mContext));
+ exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
+ mContext));
+ exposureChangesCategoryControllers.add(new ImePreferenceController(mContext));
+ exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(mContext));
+ exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(
+ mContext));
+ exposureChangesCategoryControllers.add(new CaCertsManagedProfilePreferenceController(
+ mContext));
+ controllers.addAll(exposureChangesCategoryControllers);
+ controllers.add(new PreferenceCategoryController(mContext, KEY_EXPOSURE_CHANGES_CATEGORY)
+ .setChildren(exposureChangesCategoryControllers));
+ controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(mContext));
+ controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(mContext));
+ return controllers;
+ }
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java b/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java
new file mode 100644
index 0000000..12901a6
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java
@@ -0,0 +1,78 @@
+/*
+ * 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.enterprise;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Privacy Settings preferences for a financed device. */
+public class PrivacySettingsFinancedPreference implements PrivacySettingsPreference {
+ private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+
+ private final Context mContext;
+
+ public PrivacySettingsFinancedPreference(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ /**
+ * Returns the XML Res Id that is used for financed devices in the Privacy Settings screen.
+ */
+ @Override
+ public int getPreferenceScreenResId() {
+ return R.xml.financed_privacy_settings;
+ }
+
+ /**
+ * Returns the XML resources to index for a financed device.
+ */
+ @Override
+ public List<SearchIndexableResource> getXmlResourcesToIndex() {
+ final SearchIndexableResource sir = new SearchIndexableResource(mContext);
+ sir.xmlResId = getPreferenceScreenResId();
+ return Collections.singletonList(sir);
+ }
+
+ /**
+ * Returns the preference controllers used to populate the privacy preferences in the Privacy
+ * Settings screen for a financed device.
+ */
+ @Override
+ public List<AbstractPreferenceController> createPreferenceControllers(boolean async) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new NetworkLogsPreferenceController(mContext));
+ controllers.add(new BugReportsPreferenceController(mContext));
+ controllers.add(new SecurityLogsPreferenceController(mContext));
+ final List<AbstractPreferenceController> exposureChangesCategoryControllers =
+ new ArrayList<>();
+ exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
+ mContext, async));
+ controllers.addAll(exposureChangesCategoryControllers);
+ controllers.add(new PreferenceCategoryController(mContext, KEY_EXPOSURE_CHANGES_CATEGORY)
+ .setChildren(exposureChangesCategoryControllers));
+ controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(mContext));
+ return controllers;
+ }
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsPreference.java b/src/com/android/settings/enterprise/PrivacySettingsPreference.java
new file mode 100644
index 0000000..4310f5e
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsPreference.java
@@ -0,0 +1,42 @@
+/*
+ * 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.enterprise;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.List;
+
+/** Interface for configuring what is displayed on the Privacy Settings. */
+public interface PrivacySettingsPreference {
+
+ /**
+ * Returns the XML Res Id that is used in the Privacy Settings screen.
+ */
+ int getPreferenceScreenResId();
+
+ /**
+ * Returns the XML resources to index.
+ */
+ List<SearchIndexableResource> getXmlResourcesToIndex();
+
+ /**
+ * Returns the preference controllers used to populate the privacy preferences.
+ */
+ List<AbstractPreferenceController> createPreferenceControllers(boolean async);
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.java b/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.java
new file mode 100644
index 0000000..0ec2498
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.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.enterprise;
+
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+
+/** Factory for creating the privacy settings preference for a managed device. */
+public class PrivacySettingsPreferenceFactory {
+
+ /**
+ * Determines which preference to use in the Privacy Settings based off of the type of managed
+ * device.
+ */
+ public static PrivacySettingsPreference createPrivacySettingsPreference(Context context) {
+ if (isFinancedDevice(context)) {
+ return createPrivacySettingsFinancedPreference(context);
+ } else {
+ return createPrivacySettingsEnterprisePreference(context);
+ }
+ }
+
+ private static PrivacySettingsEnterprisePreference createPrivacySettingsEnterprisePreference(
+ Context context) {
+ return new PrivacySettingsEnterprisePreference(context);
+ }
+
+ private static PrivacySettingsFinancedPreference createPrivacySettingsFinancedPreference(
+ Context context) {
+ return new PrivacySettingsFinancedPreference(context);
+ }
+
+ private static boolean isFinancedDevice(Context context) {
+ final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.isDeviceManaged() && dpm.getDeviceOwnerType(
+ dpm.getDeviceOwnerComponentOnAnyUser()) == DEVICE_OWNER_TYPE_FINANCED;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 47b2a0a..a55d0d3 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -257,6 +257,27 @@
BatteryEntry.startRequestQueue();
}
+ /**
+ * Gets the BatteryEntry list by using the supplied BatteryUsageStats.
+ */
+ public List<BatteryEntry> getBatteryEntryList(
+ BatteryUsageStats batteryUsageStats, boolean showAllApps) {
+ mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
+ if (!sConfig.shouldShowBatteryAttributionList(mContext)) {
+ return null;
+ }
+ final int dischargePercentage = getDischargePercentage(batteryUsageStats);
+ final List<BatteryEntry> usageList = getCoalescedUsageList(showAllApps);
+ final double totalPower = batteryUsageStats.getConsumedPower();
+ for (int i = 0; i < usageList.size(); i++) {
+ final BatteryEntry entry = usageList.get(i);
+ final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
+ entry.getConsumedPower(), totalPower, dischargePercentage);
+ entry.percent = percentOfTotal;
+ }
+ return usageList;
+ }
+
private int getDischargePercentage(BatteryUsageStats batteryUsageStats) {
int dischargePercentage = batteryUsageStats.getDischargePercentage();
if (dischargePercentage < 0) {
@@ -311,7 +332,7 @@
final int index = batteryEntryList.indexOfKey(realUid);
if (index < 0) {
// New entry.
- batteryEntryList.put(realUid, new BatteryEntry(mActivity, mHandler, mUserManager,
+ batteryEntryList.put(realUid, new BatteryEntry(mContext, mHandler, mUserManager,
consumer, isHidden, packages, null));
} else {
// Combine BatterySippers if we already have one with this UID.
@@ -328,7 +349,7 @@
continue;
}
- results.add(new BatteryEntry(mActivity, mHandler, mUserManager,
+ results.add(new BatteryEntry(mContext, mHandler, mUserManager,
consumer, /* isHidden */ true, null, null));
}
@@ -337,7 +358,7 @@
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,
+ results.add(new BatteryEntry(mContext, mHandler, mUserManager,
consumer, /* isHidden */ true, null, null));
}
}
diff --git a/src/com/android/settings/fuelgauge/BatteryEntry.java b/src/com/android/settings/fuelgauge/BatteryEntry.java
index 9fafefd..ed52f48 100644
--- a/src/com/android/settings/fuelgauge/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/BatteryEntry.java
@@ -35,6 +35,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.util.Pair;
import androidx.annotation.NonNull;
@@ -51,6 +52,19 @@
* and icon image.
*/
public class BatteryEntry {
+
+ public static final class NameAndIcon {
+ public final String name;
+ public final Drawable icon;
+ public final int iconId;
+
+ public NameAndIcon(String name, Drawable icon, int iconId) {
+ this.name = name;
+ this.icon = icon;
+ this.iconId = iconId;
+ }
+ }
+
public static final int MSG_UPDATE_NAME_ICON = 1;
public static final int MSG_REPORT_FULLY_DRAWN = 2;
@@ -154,9 +168,10 @@
mBatteryConsumer = batteryConsumer;
mIsHidden = isHidden;
mDefaultPackageName = packageName;
- mConsumedPower = batteryConsumer.getConsumedPower();
if (batteryConsumer instanceof UidBatteryConsumer) {
+ mConsumedPower = batteryConsumer.getConsumedPower();
+
UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
int uid = uidBatteryConsumer.getUid();
if (mDefaultPackageName == null) {
@@ -182,59 +197,18 @@
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;
- }
+ mConsumedPower = batteryConsumer.getConsumedPower()
+ - ((SystemBatteryConsumer) batteryConsumer).getPowerConsumedByApps();
+ final NameAndIcon nameAndIcon = getNameAndIconFromDrainType(
+ context, ((SystemBatteryConsumer) batteryConsumer).getDrainType());
+ iconId = nameAndIcon.iconId;
+ name = nameAndIcon.name;
} 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);
- }
+ mConsumedPower = batteryConsumer.getConsumedPower();
+ final NameAndIcon nameAndIcon = getNameAndIconFromUserId(
+ context, ((UserBatteryConsumer) batteryConsumer).getUserId());
+ icon = nameAndIcon.icon;
+ name = nameAndIcon.name;
}
if (iconId != 0) {
@@ -271,15 +245,9 @@
}
if (packages == null || packages.length == 0) {
- if (uid == 0) {
- name = mContext.getResources().getString(R.string.process_kernel_label);
- } else if ("mediaserver".equals(name)) {
- name = mContext.getResources().getString(R.string.process_mediaserver_label);
- } else if ("dex2oat".equals(name)) {
- name = mContext.getResources().getString(R.string.process_dex2oat_label);
- }
- iconId = R.drawable.ic_power_system;
- icon = mContext.getDrawable(iconId);
+ final NameAndIcon nameAndIcon = getNameAndIconFromUid(mContext, name, uid);
+ icon = nameAndIcon.icon;
+ name = nameAndIcon.name;
} else {
icon = mContext.getPackageManager().getDefaultActivityIcon();
}
@@ -452,6 +420,13 @@
}
/**
+ * Returns the BatteryConsumer of the app described by this entry.
+ */
+ public BatteryConsumer getBatteryConsumer() {
+ return mBatteryConsumer;
+ }
+
+ /**
* Returns foreground foreground time (in milliseconds) that is attributed to this entry.
*/
public long getTimeInForegroundMs() {
@@ -493,4 +468,93 @@
((UidBatteryConsumer) batteryConsumer).getPackageWithHighestDrain();
}
}
+
+ /**
+ * Gets name and icon resource from UserBatteryConsumer userId.
+ */
+ public static NameAndIcon getNameAndIconFromUserId(
+ Context context, final int userId) {
+ UserManager um = context.getSystemService(UserManager.class);
+ UserInfo info = um.getUserInfo(userId);
+
+ Drawable icon = null;
+ String name = null;
+ if (info != null) {
+ icon = Utils.getUserIcon(context, um, info);
+ name = Utils.getUserLabel(context, info);
+ } else {
+ name = context.getResources().getString(
+ R.string.running_process_item_removed_user_label);
+ }
+ return new NameAndIcon(name, icon, 0 /* iconId */);
+ }
+
+ /**
+ * Gets name and icon resource from UidBatteryConsumer uid.
+ */
+ public static NameAndIcon getNameAndIconFromUid(
+ Context context, String name, final int uid) {
+ Drawable icon = context.getDrawable(R.drawable.ic_power_system);
+ if (uid == 0) {
+ name = context.getResources().getString(R.string.process_kernel_label);
+ } else if ("mediaserver".equals(name)) {
+ name = context.getResources().getString(R.string.process_mediaserver_label);
+ } else if ("dex2oat".equals(name)) {
+ name = context.getResources().getString(R.string.process_dex2oat_label);
+ }
+ return new NameAndIcon(name, icon, 0 /* iconId */);
+ }
+
+ /**
+ * Gets name annd icon resource from SystemBatteryConsumer drain type.
+ */
+ public static NameAndIcon getNameAndIconFromDrainType(
+ Context context, final int drainType) {
+ String name = null;
+ int iconId = 0;
+ switch (drainType) {
+ 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;
+ }
+ return new NameAndIcon(name, null /* icon */, iconId);
+ }
}
diff --git a/src/com/android/settings/fuelgauge/BatterySaverController.java b/src/com/android/settings/fuelgauge/BatterySaverController.java
index fba163d..15070c3 100644
--- a/src/com/android/settings/fuelgauge/BatterySaverController.java
+++ b/src/com/android/settings/fuelgauge/BatterySaverController.java
@@ -111,7 +111,7 @@
Utils.formatPercentage(percent)) :
mContext.getString(R.string.battery_saver_off_summary);
} else {
- return mContext.getString(R.string.battery_saver_auto_routine);
+ return mContext.getString(R.string.battery_saver_pref_auto_routine_summary);
}
}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
new file mode 100644
index 0000000..11f28b7
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -0,0 +1,29 @@
+/*
+ * 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.fuelgauge;
+
+/**
+ * Feature provider for battery settings usage.
+ */
+public interface BatterySettingsFeatureProvider {
+
+ /**
+ * Get replacement activity for a given activity or fragment path.
+ */
+ String getReplacingActivityName(String originalActivity);
+
+}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
new file mode 100644
index 0000000..218d096
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -0,0 +1,36 @@
+/*
+ * 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.fuelgauge;
+
+import android.content.Context;
+
+/**
+ * Feature provider implementation for battery settings usage.
+ */
+public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider {
+
+ protected Context mContext;
+
+ public BatterySettingsFeatureProviderImpl(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ @Override
+ public String getReplacingActivityName(String originalActivity) {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 1645590..68a0b03 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -298,7 +298,7 @@
*/
public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats,
long currentTimeMs) {
- return currentTimeMs - batteryUsageStats.getStatsStartRealtime();
+ return currentTimeMs - batteryUsageStats.getStatsStartTimestamp();
}
public static void logRuntime(String tag, String message, long startTime) {
diff --git a/src/com/android/settings/fuelgauge/FakeUid.java b/src/com/android/settings/fuelgauge/FakeUid.java
index b49fb10..ee0d91f 100644
--- a/src/com/android/settings/fuelgauge/FakeUid.java
+++ b/src/com/android/settings/fuelgauge/FakeUid.java
@@ -366,6 +366,16 @@
}
@Override
+ public long getGnssMeasuredBatteryConsumptionUC() {
+ return 0;
+ }
+
+ @Override
+ public long getMobileRadioMeasuredBatteryConsumptionUC() {
+ return 0;
+ }
+
+ @Override
public long getScreenOnMeasuredBatteryConsumptionUC() {
return 0;
}
diff --git a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
index ea277a1..27b70cb 100644
--- a/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/SmartBatteryPreferenceController.java
@@ -20,27 +20,23 @@
import android.content.Context;
import android.provider.Settings;
import android.text.TextUtils;
-import android.widget.Switch;
import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.widget.MainSwitchPreference;
-import com.android.settingslib.widget.OnMainSwitchChangeListener;
/**
* Controller to change and update the smart battery toggle
*/
public class SmartBatteryPreferenceController extends BasePreferenceController implements
- OnMainSwitchChangeListener {
+ Preference.OnPreferenceChangeListener {
private static final String KEY_SMART_BATTERY = "smart_battery";
private static final int ON = 1;
private static final int OFF = 0;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
- private MainSwitchPreference mPreference;
public SmartBatteryPreferenceController(Context context) {
super(context, KEY_SMART_BATTERY);
@@ -70,19 +66,14 @@
super.updateState(preference);
final boolean smartBatteryOn = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON) == ON;
- ((MainSwitchPreference) preference).updateStatus(smartBatteryOn);
+ ((SwitchPreference) preference).setChecked(smartBatteryOn);
}
@Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mPreference = (MainSwitchPreference) screen.findPreference(getPreferenceKey());
- mPreference.addOnSwitchChangeListener(this);
- }
-
- @Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean smartBatteryOn = (Boolean) newValue;
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, isChecked ? ON : OFF);
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, smartBatteryOn ? ON : OFF);
+ return true;
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
index 9565036..790264c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceController.java
@@ -19,7 +19,6 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.os.UserManager;
-import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -34,7 +33,6 @@
*/
public class BatteryManagerPreferenceController extends BasePreferenceController {
private static final String KEY_BATTERY_MANAGER = "smart_battery_manager";
- private static final int ON = 1;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private AppOpsManager mAppOpsManager;
private UserManager mUserManager;
@@ -56,24 +54,17 @@
public void updateState(Preference preference) {
super.updateState(preference);
final int num = BatteryTipUtils.getRestrictedAppsList(mAppOpsManager, mUserManager).size();
- final String setting = mPowerUsageFeatureProvider.isSmartBatterySupported()
- ? Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
- : Settings.Global.APP_AUTO_RESTRICTION_ENABLED;
- final boolean featureOn =
- Settings.Global.getInt(mContext.getContentResolver(), setting, ON) == ON;
- updateSummary(preference, featureOn, num);
+ updateSummary(preference, num);
}
@VisibleForTesting
- void updateSummary(Preference preference, boolean featureOn, int num) {
+ void updateSummary(Preference preference, int num) {
if (num > 0) {
preference.setSummary(mContext.getResources().getQuantityString(
R.plurals.battery_manager_app_restricted, num, num));
- } else if (featureOn) {
- preference.setSummary(R.string.battery_manager_on);
} else {
- preference.setSummary(R.string.battery_manager_off);
+ preference.setSummary(R.string.battery_manager_summary);
}
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index 433c06d..623bd4f 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -28,7 +28,6 @@
import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
-import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
@@ -70,10 +69,10 @@
tips.add(new LowBatteryDetector(context, policy, batteryInfo).detect());
tips.add(new HighUsageDetector(context, policy, mBatteryUsageStats, batteryInfo).detect());
- tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
+ tips.add(new SmartBatteryDetector(
+ context, policy, batteryInfo, context.getContentResolver()).detect());
tips.add(new EarlyWarningDetector(policy, context).detect());
tips.add(new BatteryDefenderDetector(batteryInfo).detect());
- tips.add(new SummaryDetector(policy, batteryInfo.averageTimeToDischarge).detect());
// Disable this feature now since it introduces false positive cases. We will try to improve
// it in the future.
// tips.add(new RestrictAppDetector(context, policy).detect());
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 487adf8..3fbbf5b 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -230,7 +230,7 @@
}
batteryTipEnabled = mParser.getBoolean(KEY_BATTERY_TIP_ENABLED, true);
- summaryEnabled = mParser.getBoolean(KEY_SUMMARY_ENABLED, true);
+ summaryEnabled = mParser.getBoolean(KEY_SUMMARY_ENABLED, false);
batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
index 4317bff..00b1e87 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
@@ -28,11 +28,9 @@
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
-import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.CardPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.fuelgauge.EstimateKt;
import java.util.HashMap;
import java.util.List;
@@ -91,10 +89,8 @@
mPrefContext = screen.getContext();
mCardPreference = screen.findPreference(getPreferenceKey());
- // Add summary tip in advance to avoid UI flakiness
- final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW,
- EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
- summaryTip.updatePreference(mCardPreference);
+ // Set preference as invisible since there is no default tips.
+ mCardPreference.setVisible(false);
}
public void updateBatteryTips(List<BatteryTip> batteryTips) {
@@ -110,10 +106,12 @@
}
}
+ mCardPreference.setVisible(false);
for (int i = 0, size = batteryTips.size(); i < size; i++) {
final BatteryTip batteryTip = mBatteryTips.get(i);
batteryTip.validateCheck(mContext);
if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
+ mCardPreference.setVisible(true);
batteryTip.updatePreference(mCardPreference);
mBatteryTipMap.put(mCardPreference.getKey(), batteryTip);
batteryTip.log(mContext, mMetricsFeatureProvider);
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
index e88a494..d12784f 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtils.java
@@ -30,7 +30,6 @@
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.actions.BatteryDefenderAction;
-import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
import com.android.settings.fuelgauge.batterytip.actions.OpenBatterySaverAction;
import com.android.settings.fuelgauge.batterytip.actions.OpenRestrictAppFragmentAction;
@@ -99,11 +98,7 @@
return new SmartBatteryAction(settingsActivity, fragment);
case BatteryTip.TipType.BATTERY_SAVER:
case BatteryTip.TipType.LOW_BATTERY:
- if (batteryTip.getState() == BatteryTip.StateType.HANDLED) {
- return new OpenBatterySaverAction(settingsActivity);
- } else {
- return new BatterySaverAction(settingsActivity);
- }
+ return new OpenBatterySaverAction(settingsActivity);
case BatteryTip.TipType.APP_RESTRICTION:
if (batteryTip.getState() == BatteryTip.StateType.HANDLED) {
return new OpenRestrictAppFragmentAction(fragment, (RestrictAppTip) batteryTip);
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java
index 9b6b9b5..f7a4f8c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetector.java
@@ -56,7 +56,7 @@
EarlyWarningDetector.class.getName()) || mPolicy.testBatterySaverTip;
final int state = powerSaveModeOn
- ? BatteryTip.StateType.HANDLED
+ ? BatteryTip.StateType.INVISIBLE
: mPolicy.batterySaverTipEnabled && discharging && earlyWarning
? BatteryTip.StateType.NEW
: BatteryTip.StateType.INVISIBLE;
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
index ca9141d..75f47a7 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetector.java
@@ -49,16 +49,16 @@
final boolean lowBattery = mBatteryInfo.batteryLevel <= mWarningLevel
|| (mBatteryInfo.discharging && mBatteryInfo.remainingTimeUs != 0
&& mBatteryInfo.remainingTimeUs < TimeUnit.HOURS.toMicros(mPolicy.lowBatteryHour));
+ final boolean lowBatteryEnabled = mPolicy.lowBatteryEnabled && !powerSaveModeOn;
+ final boolean dischargingLowBatteryState =
+ mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery);
int state = BatteryTip.StateType.INVISIBLE;
- if (mPolicy.lowBatteryEnabled) {
- if (powerSaveModeOn) {
- // Show it is handled if battery saver is on
- state = BatteryTip.StateType.HANDLED;
- } else if (mPolicy.testLowBatteryTip || (mBatteryInfo.discharging && lowBattery)) {
- // Show it is new if in test or in discharging low battery state
- state = BatteryTip.StateType.NEW;
- }
+
+ // Show it as new if in test or in discharging low battery state,
+ // dismiss it if battery saver is on or disabled by config.
+ if (lowBatteryEnabled && dischargingLowBatteryState) {
+ state = BatteryTip.StateType.NEW;
}
return new LowBatteryTip(
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetector.java
index a8d4981..23409a1 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetector.java
@@ -17,8 +17,11 @@
package com.android.settings.fuelgauge.batterytip.detectors;
import android.content.ContentResolver;
+import android.content.Context;
+import android.os.PowerManager;
import android.provider.Settings;
+import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.SmartBatteryTip;
@@ -27,22 +30,33 @@
* Detect whether to show smart battery tip.
*/
public class SmartBatteryDetector implements BatteryTipDetector {
+ private static final int EXPECTED_BATTERY_LEVEL = 30;
+
+ private BatteryInfo mBatteryInfo;
private BatteryTipPolicy mPolicy;
private ContentResolver mContentResolver;
+ private PowerManager mPowerManager;
- public SmartBatteryDetector(BatteryTipPolicy policy, ContentResolver contentResolver) {
+ public SmartBatteryDetector(Context context, BatteryTipPolicy policy, BatteryInfo batteryInfo,
+ ContentResolver contentResolver) {
mPolicy = policy;
+ mBatteryInfo = batteryInfo;
mContentResolver = contentResolver;
+ mPowerManager = context.getSystemService(PowerManager.class);
}
@Override
public BatteryTip detect() {
- // Show it if there is no other tips shown
final boolean smartBatteryOff = Settings.Global.getInt(mContentResolver,
- Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 0
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 0;
+ final boolean isUnderExpectedBatteryLevel =
+ mBatteryInfo.batteryLevel <= EXPECTED_BATTERY_LEVEL;
+ // Show it if in test or smart battery is off.
+ final boolean enableSmartBatteryTip =
+ smartBatteryOff && !mPowerManager.isPowerSaveMode() && isUnderExpectedBatteryLevel
|| mPolicy.testSmartBatteryTip;
final int state =
- smartBatteryOff ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
+ enableSmartBatteryTip ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
return new SmartBatteryTip(state);
}
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
index 0c2bb03..6701314 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTip.java
@@ -18,7 +18,6 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.res.ColorStateList;
import android.os.Parcel;
import com.android.settings.R;
@@ -43,31 +42,23 @@
@Override
public CharSequence getTitle(Context context) {
return context.getString(
- mState == StateType.HANDLED
- ? R.string.battery_tip_early_heads_up_done_title
- : R.string.battery_tip_early_heads_up_title);
+ mState = R.string.battery_tip_early_heads_up_title);
}
@Override
public CharSequence getSummary(Context context) {
return context.getString(
- mState == StateType.HANDLED
- ? R.string.battery_tip_early_heads_up_done_summary
- : R.string.battery_tip_early_heads_up_summary);
+ mState = R.string.battery_tip_early_heads_up_summary);
}
@Override
public int getIconId() {
- return mState == StateType.HANDLED
- ? R.drawable.ic_battery_status_maybe_24dp
- : R.drawable.ic_battery_status_bad_24dp;
+ return mState = R.drawable.ic_battery_status_bad_24dp;
}
@Override
public int getIconTintColorId() {
- return mState == StateType.HANDLED
- ? R.color.battery_maybe_color_light
- : R.color.battery_bad_color_light;
+ return mState = R.color.battery_bad_color_light;
}
@Override
@@ -76,9 +67,9 @@
if (earlyWarningTip.mState == StateType.NEW) {
// Display it if there is early warning
mState = StateType.NEW;
- } else if (mState == StateType.NEW && earlyWarningTip.mState == StateType.INVISIBLE) {
- // If powerSaveMode is really on, show it as handled, otherwise just dismiss it.
- mState = earlyWarningTip.mPowerSaveModeOn ? StateType.HANDLED : StateType.INVISIBLE;
+ } else if (earlyWarningTip.mPowerSaveModeOn) {
+ // If powerSaveMode is really on, dismiss it.
+ mState = StateType.INVISIBLE;
} else {
mState = earlyWarningTip.getState();
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
index d7acdd2..7790b10 100644
--- a/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
+++ b/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTip.java
@@ -21,7 +21,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.settings.R;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
/**
@@ -43,8 +42,7 @@
@Override
public CharSequence getSummary(Context context) {
- return mState == StateType.HANDLED ? context.getString(
- R.string.battery_tip_early_heads_up_done_summary) : mSummary;
+ return mSummary;
}
@Override
diff --git a/src/com/android/settings/gestures/LongPressPowerButtonPreferenceController.java b/src/com/android/settings/gestures/LongPressPowerButtonPreferenceController.java
new file mode 100644
index 0000000..7a45602
--- /dev/null
+++ b/src/com/android/settings/gestures/LongPressPowerButtonPreferenceController.java
@@ -0,0 +1,188 @@
+/*
+ * 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.gestures;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Configures the behaviour of long press power button action.
+ */
+public class LongPressPowerButtonPreferenceController extends TogglePreferenceController implements
+ LifecycleObserver {
+
+ private static final String POWER_BUTTON_LONG_PRESS_SETTING =
+ Settings.Global.POWER_BUTTON_LONG_PRESS;
+ private static final Uri POWER_BUTTON_LONG_PRESS_SETTING_URI =
+ Settings.Global.getUriFor(POWER_BUTTON_LONG_PRESS_SETTING);
+
+ // Used for fallback to global actions if necessary.
+ @VisibleForTesting
+ static final String CARDS_AVAILABLE_KEY =
+ Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE;
+ @VisibleForTesting
+ static final String CARDS_ENABLED_KEY = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED;
+
+ /**
+ * Value used for long press power button behaviour when Assist setting is enabled.
+ *
+ * {@link com.android.server.policy.PhoneWindowManager#LONG_PRESS_POWER_GLOBAL_ACTIONS} for
+ * source of the value.
+ */
+ @VisibleForTesting
+ static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
+ @VisibleForTesting
+ static final int LONG_PRESS_POWER_SHUT_OFF = 2;
+ @VisibleForTesting
+ static final int LONG_PRESS_POWER_ASSISTANT_VALUE = 5; // Settings.Secure.ASSISTANT
+
+ /**
+ * Value used for long press power button behaviour when the Assist setting is disabled.
+ *
+ * If this value matches Assist setting, then it falls back to Global Actions panel or
+ * power menu, depending on their respective settings.
+ */
+ private static final int POWER_BUTTON_LONG_PRESS_DEFAULT_VALUE_RESOURCE =
+ R.integer.config_longPressOnPowerBehavior;
+
+ @Nullable
+ private SettingObserver mSettingsObserver;
+
+ public LongPressPowerButtonPreferenceController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mSettingsObserver = new SettingObserver(screen.findPreference(getPreferenceKey()));
+ }
+
+ /**
+ * Called when the settings pages resumes.
+ */
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ public void onResume() {
+ if (mSettingsObserver != null) {
+ mSettingsObserver.register();
+ }
+ }
+
+ /**
+ * Called when the settings page pauses.
+ */
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ public void onPause() {
+ if (mSettingsObserver != null) {
+ mSettingsObserver.unregister();
+ }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ final boolean enabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable);
+ return enabled ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ final int powerButtonValue = Settings.Global.getInt(mContext.getContentResolver(),
+ POWER_BUTTON_LONG_PRESS_SETTING,
+ mContext.getResources().getInteger(POWER_BUTTON_LONG_PRESS_DEFAULT_VALUE_RESOURCE));
+ return powerButtonValue == LONG_PRESS_POWER_ASSISTANT_VALUE;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ if (isChecked) {
+ return Settings.Global.putInt(mContext.getContentResolver(),
+ POWER_BUTTON_LONG_PRESS_SETTING, LONG_PRESS_POWER_ASSISTANT_VALUE);
+ }
+
+ // We need to determine the right disabled value - we set it to device default
+ // if it's different than Assist, otherwise we fallback to either global actions or power
+ //menu.
+ final int defaultPowerButtonValue = mContext.getResources().getInteger(
+ POWER_BUTTON_LONG_PRESS_DEFAULT_VALUE_RESOURCE);
+ if (defaultPowerButtonValue == LONG_PRESS_POWER_ASSISTANT_VALUE) {
+ final int fallbackValue = isCardsOrControlsAvailable() ? LONG_PRESS_POWER_GLOBAL_ACTIONS
+ : LONG_PRESS_POWER_SHUT_OFF;
+ return Settings.Global.putInt(mContext.getContentResolver(),
+ POWER_BUTTON_LONG_PRESS_SETTING, fallbackValue);
+ }
+
+ return Settings.Global.putInt(mContext.getContentResolver(),
+ POWER_BUTTON_LONG_PRESS_SETTING, defaultPowerButtonValue);
+ }
+
+ /**
+ * Returns true if the global actions menu on power button click is enabled via any of the
+ * content options.
+ */
+ private boolean isCardsOrControlsAvailable() {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final boolean cardsAvailable = Settings.Secure.getInt(resolver, CARDS_AVAILABLE_KEY, 0)
+ != 0;
+ final boolean controlsAvailable = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_CONTROLS);
+ return cardsAvailable || controlsAvailable;
+ }
+
+ private final class SettingObserver extends ContentObserver {
+
+ private final Preference mPreference;
+
+ SettingObserver(Preference preference) {
+ super(new Handler(Looper.getMainLooper()));
+ mPreference = preference;
+ }
+
+ public void register() {
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(POWER_BUTTON_LONG_PRESS_SETTING_URI, false, this);
+ }
+
+ public void unregister() {
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateState(mPreference);
+ }
+ }
+
+}
diff --git a/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java b/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
index c6db4ea..03bd195 100644
--- a/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
+++ b/src/com/android/settings/gestures/OneHandedEnablePreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.gestures;
import android.content.Context;
-import android.os.SystemProperties;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
@@ -28,15 +27,13 @@
**/
public class OneHandedEnablePreferenceController extends TogglePreferenceController {
- static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
-
public OneHandedEnablePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public int getAvailabilityStatus() {
- return SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)
+ return OneHandedSettingsUtils.isSupportOneHandedMode()
? BasePreferenceController.AVAILABLE
: BasePreferenceController.UNSUPPORTED_ON_DEVICE;
}
@@ -45,6 +42,7 @@
public boolean setChecked(boolean isChecked) {
OneHandedSettingsUtils.setSettingsOneHandedModeEnabled(mContext,
isChecked);
+ OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, !isChecked);
return true;
}
diff --git a/src/com/android/settings/gestures/OneHandedSettings.java b/src/com/android/settings/gestures/OneHandedSettings.java
index 2449cf2..6a07c85 100644
--- a/src/com/android/settings/gestures/OneHandedSettings.java
+++ b/src/com/android/settings/gestures/OneHandedSettings.java
@@ -18,7 +18,6 @@
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.os.SystemProperties;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
@@ -52,8 +51,7 @@
new BaseSearchIndexProvider(R.xml.one_handed_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
- return SystemProperties.getBoolean(
- OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, false);
+ return OneHandedSettingsUtils.isSupportOneHandedMode();
}
};
}
diff --git a/src/com/android/settings/gestures/OneHandedSettingsUtils.java b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
index f3d7e33..ed0f62f 100644
--- a/src/com/android/settings/gestures/OneHandedSettingsUtils.java
+++ b/src/com/android/settings/gestures/OneHandedSettingsUtils.java
@@ -22,6 +22,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.provider.Settings;
/**
@@ -29,6 +30,8 @@
*/
public class OneHandedSettingsUtils {
+ static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
public enum OneHandedTimeout {
NEVER(0), SHORT(4), MEDIUM(8), LONG(12);
@@ -52,6 +55,13 @@
}
/**
+ * Get One-Handed mode support flag.
+ */
+ public static boolean isSupportOneHandedMode() {
+ return SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
+ }
+
+ /**
* Get one-handed mode enable or disable flag from Settings provider.
*
* @param context App context
@@ -119,6 +129,28 @@
}
/**
+ * Get Swipe-down-notification enable or disable flag from Settings provider.
+ *
+ * @param context App context
+ * @return enable or disable Swipe-down-notification flag.
+ */
+ public static boolean isSwipeDownNotificationEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0) == 1;
+ }
+
+ /**
+ * Set Swipe-down-notification enable or disable flag to Settings provider.
+ *
+ * @param context App context
+ * @param enable enable or disable Swipe-down-notification.
+ */
+ public static void setSwipeDownNotificationEnabled(Context context, boolean enable) {
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, enable ? 1 : 0);
+ }
+
+ /**
* Register callback for observing Settings.Secure.ONE_HANDED_MODE_ENABLED state.
* @param callback for state changes
*/
diff --git a/src/com/android/settings/gestures/PowerMenuPreferenceController.java b/src/com/android/settings/gestures/PowerMenuPreferenceController.java
index 6ef583b..b11ba65 100644
--- a/src/com/android/settings/gestures/PowerMenuPreferenceController.java
+++ b/src/com/android/settings/gestures/PowerMenuPreferenceController.java
@@ -57,7 +57,8 @@
@Override
public int getAvailabilityStatus() {
- return isCardsAvailable() || isControlsAvailable() ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ return isCardsAvailable() || isControlsAvailable() || isAssistInvocationAvailable()
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
private boolean isControlsAvailable() {
@@ -68,4 +69,9 @@
return Settings.Secure.getInt(mContext.getContentResolver(),
CARDS_AVAILABLE_SETTING, 0) == 1;
}
+
+ private boolean isAssistInvocationAvailable() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable);
+ }
}
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
index 04a32de..28441cd 100644
--- a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
@@ -16,14 +16,8 @@
package com.android.settings.gestures;
-import static android.provider.Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
-
-import static com.android.settings.gestures.OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE;
import android.content.Context;
-import android.os.SystemProperties;
-import android.provider.Settings;
-import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -33,32 +27,20 @@
**/
public class SwipeBottomToNotificationPreferenceController extends TogglePreferenceController {
- private static final int ON = 1;
- private static final int OFF = 0;
-
private static final String PREF_KEY = "gesture_swipe_bottom_to_notification";
public SwipeBottomToNotificationPreferenceController(Context context, String key) {
super(context, key);
}
- /** Indicates whether the gesture is available or not. */
- public static boolean isGestureAvailable(Context context) {
- // Disable the gesture once One-Handed mode gesture enabled.
- if (SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
- return !OneHandedSettingsUtils.isOneHandedModeEnabled(context);
- }
- return true;
- }
-
@Override
public int getAvailabilityStatus() {
- return isGestureAvailable(mContext) ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
+ return OneHandedSettingsUtils.isSupportOneHandedMode() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public boolean isSliceable() {
- return TextUtils.equals(getPreferenceKey(), PREF_KEY);
+ return true;
}
@Override
@@ -68,15 +50,16 @@
@Override
public boolean setChecked(boolean isChecked) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, isChecked ? ON : OFF);
+ if (isChecked) {
+ OneHandedSettingsUtils.setSettingsOneHandedModeEnabled(mContext, false);
+ }
+ OneHandedSettingsUtils.setSwipeDownNotificationEnabled(mContext, isChecked);
return true;
}
@Override
public boolean isChecked() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, ON) == ON;
+ return OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext);
}
@Override
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java b/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
index d0441f3..9d85f11 100644
--- a/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
+++ b/src/com/android/settings/gestures/SwipeBottomToNotificationSettings.java
@@ -50,8 +50,10 @@
@Override
protected boolean isPageSearchEnabled(Context context) {
- return SwipeBottomToNotificationPreferenceController
- .isGestureAvailable(context);
+ if (!OneHandedSettingsUtils.isSupportOneHandedMode()) {
+ return false;
+ }
+ return !OneHandedSettingsUtils.isOneHandedModeEnabled(context);
}
};
}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index aa7b2d1..881e39c 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -43,6 +43,25 @@
private static final String TAG = "SettingsHomepageActivity";
+ private static final long HOMEPAGE_LOADING_TIMEOUT_MS = 300;
+
+ private View mHomepageView;
+ private View mSuggestionView;
+
+ /**
+ * Shows the homepage and shows/hides the suggestion together. Only allows to be executed once
+ * to avoid the flicker caused by the suggestion suddenly appearing/disappearing.
+ */
+ public void showHomepageWithSuggestion(boolean showSuggestion) {
+ if (mHomepageView == null) {
+ return;
+ }
+ Log.i(TAG, "showHomepageWithSuggestion: " + showSuggestion);
+ mSuggestionView.setVisibility(showSuggestion ? View.VISIBLE : View.GONE);
+ mHomepageView.setVisibility(View.VISIBLE);
+ mHomepageView = null;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -56,15 +75,23 @@
FeatureFactory.getFactory(this).getSearchFeatureProvider()
.initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);
- final ImageView avatarView = findViewById(R.id.account_avatar);
- getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));
if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
- // Only allow contextual features on high ram devices.
+ // Only allow features on high ram devices.
+ final ImageView avatarView = findViewById(R.id.account_avatar);
+ if (AvatarViewMixin.isAvatarSupported(this)) {
+ avatarView.setVisibility(View.VISIBLE);
+ getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
+ }
+
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.SILKY_HOME)) {
showSuggestionFragment();
+ } else {
+ findViewById(R.id.homepage_title).setVisibility(View.GONE);
+ avatarView.setVisibility(View.GONE);
}
+
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
}
@@ -81,9 +108,16 @@
return;
}
+ mSuggestionView = findViewById(R.id.suggestion_content);
+ mHomepageView = findViewById(R.id.settings_homepage_container);
+ // Hide the homepage for preparing the suggestion.
+ mHomepageView.setVisibility(View.GONE);
+ // Schedule a timer to show the homepage and hide the suggestion on timeout.
+ mHomepageView.postDelayed(() -> showHomepageWithSuggestion(false),
+ HOMEPAGE_LOADING_TIMEOUT_MS);
try {
- showFragment(fragment.newInstance(), R.id.contextual_suggestion_content);
- } catch (IllegalAccessException | InstantiationException e) {
+ showFragment(fragment.getConstructor().newInstance(), R.id.suggestion_content);
+ } catch (Exception e) {
Log.w(TAG, "Cannot show fragment", e);
}
}
@@ -110,10 +144,7 @@
private int getSearchBoxHeight() {
final int searchBarHeight = getResources().getDimensionPixelSize(R.dimen.search_bar_height);
- final int searchBarMarginTop = getResources().getDimensionPixelSize(
- R.dimen.search_bar_margin);
- final int searchBarMarginBottom = getResources().getDimensionPixelSize(
- R.dimen.search_bar_margin_bottom);
- return searchBarHeight + searchBarMarginTop + searchBarMarginBottom;
+ final int searchBarMargin = getResources().getDimensionPixelSize(R.dimen.search_bar_margin);
+ return searchBarHeight + searchBarMargin * 2;
}
}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/LowStorageSlice.java b/src/com/android/settings/homepage/contextualcards/slices/LowStorageSlice.java
index 576b435..3812534 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/LowStorageSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/LowStorageSlice.java
@@ -33,7 +33,7 @@
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.Utils;
-import com.android.settings.deviceinfo.StorageSettings;
+import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBuilderUtils;
@@ -107,7 +107,7 @@
.toString();
return SliceBuilderUtils.buildSearchResultPageIntent(mContext,
- StorageSettings.class.getName(), "" /* key */,
+ StorageDashboardFragment.class.getName(), "" /* key */,
screenTitle,
SettingsEnums.SLICE)
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
diff --git a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
index 686558c..d41a378 100644
--- a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
@@ -91,11 +91,19 @@
List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
final Context context = getPrefContext();
final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
+ final List<InputMethodInfo> enabledImis = mImm.getEnabledInputMethodList();
final int numImis = (imis == null ? 0 : imis.size());
for (int i = 0; i < numImis; ++i) {
final InputMethodInfo imi = imis.get(i);
+ // TODO (b/182876800): Move this logic out of isAllowedByOrganization and
+ // into a new boolean.
+ // If an input method is enabled but not included in the permitted list, then set it as
+ // allowed by organization. Doing so will allow the user to disable the input method and
+ // remain complaint with the organization's policy. Once disabled, the input method
+ // cannot be re-enabled because it is not in the permitted list.
final boolean isAllowedByOrganization = permittedList == null
- || permittedList.contains(imi.getPackageName());
+ || permittedList.contains(imi.getPackageName())
+ || enabledImis.contains(imi);
final InputMethodPreference pref = new InputMethodPreference(
context, imi, true, isAllowedByOrganization, this);
pref.setIcon(imi.loadIcon(context.getPackageManager()));
diff --git a/src/com/android/settings/location/LocationFooterPreferenceController.java b/src/com/android/settings/location/LocationFooterPreferenceController.java
deleted file mode 100644
index 3b9324d..0000000
--- a/src/com/android/settings/location/LocationFooterPreferenceController.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.location.LocationManager;
-import android.util.Log;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-
-import com.android.settingslib.widget.FooterPreference;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Preference controller for location footer preference category
- */
-public class LocationFooterPreferenceController extends LocationBasePreferenceController {
-
- private static final String TAG = "LocationFooter";
- private static final Intent INJECT_INTENT =
- new Intent(LocationManager.SETTINGS_FOOTER_DISPLAYED_ACTION);
-
- private final PackageManager mPackageManager;
-
- public LocationFooterPreferenceController(Context context, String key) {
- super(context, key);
- mPackageManager = context.getPackageManager();
- }
-
- /**
- * Insert footer preferences.
- */
- @Override
- public void updateState(Preference preference) {
- PreferenceCategory category = (PreferenceCategory) preference;
- category.removeAll();
- Collection<FooterData> footerData = getFooterData();
- for (FooterData data : footerData) {
- try {
- String footerString =
- mPackageManager
- .getResourcesForApplication(data.applicationInfo)
- .getString(data.footerStringRes);
-
- // Generate a footer preference with the given text
- FooterPreference footerPreference = new FooterPreference(preference.getContext());
- footerPreference.setTitle(footerString);
- category.addPreference(footerPreference);
- } catch (NameNotFoundException exception) {
- Log.w(
- TAG,
- "Resources not found for application "
- + data.applicationInfo.packageName);
- }
- }
- }
-
- /**
- * Do nothing on location mode changes.
- */
- @Override
- public void onLocationModeChanged(int mode, boolean restricted) {}
-
- /**
- * Location footer preference group should be displayed if there is at least one footer to
- * inject.
- */
- @Override
- public int getAvailabilityStatus() {
- return !getFooterData().isEmpty() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
- }
-
- /**
- * Return a list of strings with text provided by ACTION_INJECT_FOOTER broadcast receivers.
- */
- private List<FooterData> getFooterData() {
- // Fetch footer text from system apps
- List<ResolveInfo> resolveInfos =
- mPackageManager.queryBroadcastReceivers(
- INJECT_INTENT, PackageManager.GET_META_DATA);
- if (resolveInfos == null) {
- Log.e(TAG, "Unable to resolve intent " + INJECT_INTENT);
- return Collections.emptyList();
- }
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Found broadcast receivers: " + resolveInfos);
- }
-
- List<FooterData> footerDataList = new ArrayList<>(resolveInfos.size());
- for (ResolveInfo resolveInfo : resolveInfos) {
- ActivityInfo activityInfo = resolveInfo.activityInfo;
- ApplicationInfo appInfo = activityInfo.applicationInfo;
-
- // If a non-system app tries to inject footer, ignore it
- if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Log.w(TAG, "Ignoring attempt to inject footer from app not in system image: "
- + resolveInfo);
- continue;
- }
-
- // Get the footer text resource id from broadcast receiver's metadata
- if (activityInfo.metaData == null) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "No METADATA in broadcast receiver " + activityInfo.name);
- }
- continue;
- }
-
- final int footerTextRes =
- activityInfo.metaData.getInt(LocationManager.METADATA_SETTINGS_FOOTER_STRING);
- if (footerTextRes == 0) {
- Log.w(
- TAG,
- "No mapping of integer exists for "
- + LocationManager.METADATA_SETTINGS_FOOTER_STRING);
- continue;
- }
- footerDataList.add(new FooterData(footerTextRes, appInfo));
- }
- return footerDataList;
- }
-
- /**
- * Contains information related to a footer.
- */
- private static class FooterData {
-
- // The string resource of the footer
- final int footerStringRes;
-
- // Application info of receiver injecting this footer
- final ApplicationInfo applicationInfo;
-
- FooterData(int footerRes, ApplicationInfo appInfo) {
- this.footerStringRes = footerRes;
- this.applicationInfo = appInfo;
- }
- }
-}
diff --git a/src/com/android/settings/location/LocationPersonalSettings.java b/src/com/android/settings/location/LocationPersonalSettings.java
index bdf2d2b..ef5465c 100644
--- a/src/com/android/settings/location/LocationPersonalSettings.java
+++ b/src/com/android/settings/location/LocationPersonalSettings.java
@@ -52,7 +52,6 @@
use(AppLocationPermissionPreferenceController.class).init(this);
// 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);
diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java
index bb971bf..b5105ed 100644
--- a/src/com/android/settings/location/LocationSettings.java
+++ b/src/com/android/settings/location/LocationSettings.java
@@ -84,9 +84,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);
}
@Override
diff --git a/src/com/android/settings/location/LocationWorkProfileSettings.java b/src/com/android/settings/location/LocationWorkProfileSettings.java
index 6783075..4cafcbf 100644
--- a/src/com/android/settings/location/LocationWorkProfileSettings.java
+++ b/src/com/android/settings/location/LocationWorkProfileSettings.java
@@ -50,7 +50,6 @@
super.onAttach(context);
use(AppLocationPermissionPreferenceController.class).init(this);
- use(LocationFooterPreferenceController.class).init(this);
use(LocationForWorkPreferenceController.class).init(this);
use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
diff --git a/src/com/android/settings/network/InternetPreferenceController.java b/src/com/android/settings/network/InternetPreferenceController.java
index a6c8574..639bab5 100644
--- a/src/com/android/settings/network/InternetPreferenceController.java
+++ b/src/com/android/settings/network/InternetPreferenceController.java
@@ -75,7 +75,7 @@
private static Map<Integer, Integer> sSummaryMap = new HashMap<>();
static {
sSummaryMap.put(INTERNET_OFF, R.string.condition_airplane_title);
- sSummaryMap.put(INTERNET_NETWORKS_AVAILABLE, R.string.disconnected);
+ sSummaryMap.put(INTERNET_NETWORKS_AVAILABLE, R.string.networks_available);
sSummaryMap.put(INTERNET_WIFI, 0);
sSummaryMap.put(INTERNET_CELLULAR, 0);
sSummaryMap.put(INTERNET_ETHERNET, R.string.to_switch_networks_disconnect_ethernet);
diff --git a/src/com/android/settings/network/InternetUpdater.java b/src/com/android/settings/network/InternetUpdater.java
index 2f704e7..dd70cef 100644
--- a/src/com/android/settings/network/InternetUpdater.java
+++ b/src/com/android/settings/network/InternetUpdater.java
@@ -32,7 +32,6 @@
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
-import android.net.NetworkCapabilities.Transport;
import android.net.wifi.WifiManager;
import android.util.Log;
@@ -132,7 +131,7 @@
@VisibleForTesting
boolean mInternetAvailable;
@VisibleForTesting
- @Transport int mTransport;
+ int mTransport;
private static Map<Integer, Integer> sTransportMap = new HashMap<>();
static {
sTransportMap.put(TRANSPORT_WIFI, INTERNET_WIFI);
@@ -143,13 +142,13 @@
private NetworkCallback mNetworkCallback = new NetworkCallback() {
public void onCapabilitiesChanged(@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities) {
- checkNetworkCapabilities(networkCapabilities);
+ updateInternetAvailable(networkCapabilities);
}
@Override
public void onLost(@NonNull Network network) {
mInternetAvailable = false;
- update();
+ updateInternetType();
}
};
@@ -204,7 +203,7 @@
Network activeNetwork = mConnectivityManager.getActiveNetwork();
if (activeNetwork == null) {
mInternetAvailable = false;
- update();
+ updateInternetType();
return;
}
@@ -212,36 +211,34 @@
mConnectivityManager.getNetworkCapabilities(activeNetwork);
if (activeNetworkCapabilities == null) {
mInternetAvailable = false;
- update();
+ updateInternetType();
return;
}
- checkNetworkCapabilities(activeNetworkCapabilities);
- }
-
- private void checkNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
- if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- mInternetAvailable = false;
- update();
- return;
- }
-
- boolean internetAvailable = false;
- for (@Transport int transport : networkCapabilities.getTransportTypes()) {
- if (sTransportMap.containsKey(transport)) {
- mTransport = transport;
- internetAvailable = true;
- Log.i(TAG, "Detect an internet capability network with transport type: "
- + mTransport);
- break;
- }
- }
- mInternetAvailable = internetAvailable;
- update();
+ updateInternetAvailable(activeNetworkCapabilities);
}
@VisibleForTesting
- void update() {
+ void updateInternetAvailable(@NonNull NetworkCapabilities capabilities) {
+ boolean internetAvailable = false;
+ if (capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
+ for (int transport : capabilities.getTransportTypes()) {
+ if (sTransportMap.containsKey(transport)) {
+ mTransport = transport;
+ internetAvailable = true;
+ Log.i(TAG, "Detect an internet available network with transport type: "
+ + mTransport);
+ break;
+ }
+ }
+ }
+ mInternetAvailable = internetAvailable;
+ updateInternetType();
+ }
+
+ @VisibleForTesting
+ void updateInternetType() {
@InternetType int internetType = INTERNET_NETWORKS_AVAILABLE;
if (mInternetAvailable) {
internetType = sTransportMap.get(mTransport);
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index 803b981..1d8ee7a 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -1129,7 +1129,7 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MenuItem item = menu.add(0, MENU_FIX_CONNECTIVITY, 0, R.string.fix_connectivity);
- item.setIcon(R.drawable.ic_refresh_24dp);
+ item.setIcon(R.drawable.ic_repair_24dp);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
super.onCreateOptionsMenu(menu, inflater);
}
diff --git a/src/com/android/settings/network/ProviderModelSlice.java b/src/com/android/settings/network/ProviderModelSlice.java
index 18765a8..beb7489 100644
--- a/src/com/android/settings/network/ProviderModelSlice.java
+++ b/src/com/android/settings/network/ProviderModelSlice.java
@@ -21,6 +21,7 @@
import static com.android.settings.slices.CustomSliceRegistry.PROVIDER_MODEL_SLICE_URI;
+import android.annotation.ColorInt;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
@@ -30,6 +31,7 @@
import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
@@ -83,9 +85,10 @@
// Second section: Add a carrier item.
// Third section: Add the Wi-Fi items which are not connected.
// Fourth section: If device has connection problem, this row show the message for user.
- boolean hasEthernet = isEthernetConnected();
+ @InternetUpdater.InternetType int internetType = getInternetType();
final ListBuilder listBuilder = mHelper.createListBuilder(getUri());
- if (mHelper.isAirplaneModeEnabled() && !mWifiManager.isWifiEnabled() && !hasEthernet) {
+ if (mHelper.isAirplaneModeEnabled() && !mWifiManager.isWifiEnabled()
+ && internetType != InternetUpdater.INTERNET_ETHERNET) {
log("Airplane mode is enabled.");
return listBuilder.build();
}
@@ -105,15 +108,17 @@
log("hasCarrier: " + hasCarrier);
// First section: Add a Ethernet or Wi-Fi item which state is connected.
+ boolean isConnectedWifiAddedTop = false;
final WifiSliceItem connectedWifiItem = mHelper.getConnectedWifiItem(wifiList);
- if (hasEthernet) {
+ if (internetType == InternetUpdater.INTERNET_ETHERNET) {
log("get Ethernet item which is connected");
listBuilder.addRow(createEthernetRow());
maxListSize--;
} else {
- if (connectedWifiItem != null) {
- log("get Wi-Fi item which is connected");
+ if (connectedWifiItem != null && internetType == InternetUpdater.INTERNET_WIFI) {
+ log("get Wi-Fi item which is connected to internet");
listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
+ isConnectedWifiAddedTop = true;
maxListSize--;
}
}
@@ -128,7 +133,7 @@
}
// Third section: Add the connected Wi-Fi item to Wi-Fi list if the Ethernet is connected.
- if (connectedWifiItem != null && hasEthernet) {
+ if (connectedWifiItem != null && !isConnectedWifiAddedTop) {
log("get Wi-Fi item which is connected");
listBuilder.addRow(getWifiSliceItemRow(connectedWifiItem));
maxListSize--;
@@ -222,12 +227,12 @@
return SliceBackgroundWorker.getInstance(getUri());
}
- private boolean isEthernetConnected() {
+ private @InternetUpdater.InternetType int getInternetType() {
final NetworkProviderWorker worker = getWorker();
if (worker == null) {
- return false;
+ return InternetUpdater.INTERNET_NETWORKS_AVAILABLE;
}
- return worker.isEthernetConnected();
+ return worker.getInternetType();
}
@VisibleForTesting
@@ -243,6 +248,20 @@
.setSubtitle(mContext.getText(R.string.to_switch_networks_disconnect_ethernet));
}
+ @Override
+ protected IconCompat getWifiSliceItemLevelIcon(WifiSliceItem wifiSliceItem) {
+ if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED
+ && getInternetType() != InternetUpdater.INTERNET_WIFI) {
+ final @ColorInt int tint = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.colorControlNormal);
+ final Drawable drawable = mContext.getDrawable(
+ Utils.getWifiIconResource(wifiSliceItem.getLevel()));
+ drawable.setTint(tint);
+ return Utils.createIconWithDrawable(drawable);
+ }
+ return super.getWifiSliceItemLevelIcon(wifiSliceItem);
+ }
+
/**
* Wrap the subscriptionManager call for test mocking.
*/
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index 440d425..3a0ceeac 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -24,6 +24,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.net.wifi.WifiManager;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
@@ -231,6 +232,7 @@
}
private String getMobileSummary(String networkTypeDescription) {
+ final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
String summary = networkTypeDescription;
if (isDataSimActive()) {
summary = mContext.getString(R.string.preference_summary_default_combination,
@@ -238,6 +240,8 @@
networkTypeDescription);
} else if (!isMobileDataEnabled()) {
summary = mContext.getString(R.string.mobile_data_off_summary);
+ } else if (!wifiManager.isWifiEnabled() && !isDataSimActive()) {
+ summary = mContext.getString(R.string.mobile_data_no_connection);
}
return summary;
}
diff --git a/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index e815d49..0bec7ac 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -27,6 +27,8 @@
import android.os.UserManager;
import android.provider.Settings;
import android.provider.SettingsSlicesContract;
+import android.security.Credentials;
+import android.security.LegacyVpnProfileStore;
import android.util.Log;
import android.util.SparseArray;
@@ -36,8 +38,11 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnProfile;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.vpn2.VpnInfoPreference;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -119,6 +124,7 @@
// Copied from SystemUI::SecurityControllerImpl
SparseArray<VpnConfig> vpns = new SparseArray<>();
final List<UserInfo> users = mUserManager.getUsers();
+ int connectedLegacyVpnCount = 0;
for (UserInfo user : users) {
VpnConfig cfg = mVpnManager.getVpnConfig(user.id);
if (cfg == null) {
@@ -129,6 +135,8 @@
final LegacyVpnInfo legacyVpn = mVpnManager.getLegacyVpnInfo(user.id);
if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
continue;
+ } else {
+ connectedLegacyVpnCount++;
}
}
vpns.put(user.id, cfg);
@@ -141,13 +149,34 @@
uid = userInfo.id;
}
VpnConfig vpn = vpns.get(uid);
- final String summary;
+ String summary;
if (vpn == null) {
summary = mContext.getString(R.string.vpn_disconnected_summary);
} else {
summary = getNameForVpnConfig(vpn, UserHandle.of(uid));
}
- ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary));
+ // Optionally add warning icon if an insecure VPN is present.
+ if (Utils.isProviderModelEnabled(mContext) && mPreference instanceof VpnInfoPreference) {
+ final int insecureVpnCount = getInsecureVpnCount();
+ boolean isInsecureVPN = insecureVpnCount > 0;
+ ((VpnInfoPreference) mPreference).setInsecureVpn(isInsecureVPN);
+ // Set the summary based on the total number of VPNs and insecure VPNs.
+ if (isInsecureVPN) {
+ // Add the users and the number of legacy vpns to determine if there is more than
+ // one vpn, since there can be more than one VPN per user.
+ final int vpnCount = vpns.size()
+ + LegacyVpnProfileStore.list(Credentials.VPN).length
+ - connectedLegacyVpnCount;
+ if (vpnCount == 1) {
+ summary = mContext.getString(R.string.vpn_settings_insecure_single);
+ } else {
+ summary = mContext.getString(
+ R.string.vpn_settings_insecure_multiple, insecureVpnCount);
+ }
+ }
+ }
+ final String finalSummary = summary;
+ ThreadUtils.postOnMainThread(() -> mPreference.setSummary(finalSummary));
}
@VisibleForTesting
@@ -167,6 +196,21 @@
}
}
+ @VisibleForTesting
+ protected int getInsecureVpnCount() {
+ int count = 0;
+ for (String key : LegacyVpnProfileStore.list(Credentials.VPN)) {
+ final VpnProfile profile = VpnProfile.decode(key,
+ LegacyVpnProfileStore.get(Credentials.VPN + key));
+ // Return whether any profile is an insecure type.
+ if (VpnProfile.isLegacyType(profile.type)) {
+ count++;
+ }
+ }
+ // We did not find any insecure VPNs.
+ return count;
+ }
+
// Copied from SystemUI::SecurityControllerImpl
private final ConnectivityManager.NetworkCallback
mNetworkCallback = new ConnectivityManager.NetworkCallback() {
diff --git a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java
index cfd587a..a4cff33 100644
--- a/src/com/android/settings/network/telephony/NetworkOperatorPreference.java
+++ b/src/com/android/settings/network/telephony/NetworkOperatorPreference.java
@@ -19,6 +19,7 @@
import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
import android.content.Context;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
@@ -180,7 +181,7 @@
public OperatorInfo getOperatorInfo() {
return new OperatorInfo(Objects.toString(mCellId.getOperatorAlphaLong(), ""),
Objects.toString(mCellId.getOperatorAlphaShort(), ""),
- getOperatorNumeric());
+ getOperatorNumeric(), getAccessNetworkTypeFromCellInfo(mCellInfo));
}
private int getIconIdForCell(CellInfo ci) {
@@ -225,6 +226,25 @@
return null;
}
+ private int getAccessNetworkTypeFromCellInfo(CellInfo ci) {
+ if (ci instanceof CellInfoGsm) {
+ return AccessNetworkType.GERAN;
+ }
+ if (ci instanceof CellInfoCdma) {
+ return AccessNetworkType.CDMA2000;
+ }
+ if ((ci instanceof CellInfoWcdma) || (ci instanceof CellInfoTdscdma)) {
+ return AccessNetworkType.UTRAN;
+ }
+ if (ci instanceof CellInfoLte) {
+ return AccessNetworkType.EUTRAN;
+ }
+ if (ci instanceof CellInfoNr) {
+ return AccessNetworkType.NGRAN;
+ }
+ return AccessNetworkType.UNKNOWN;
+ }
+
private void updateIcon(int level) {
if (!mUseNewApi || level < 0 || level >= NUM_SIGNAL_STRENGTH_BINS) {
return;
diff --git a/src/com/android/settings/network/telephony/NetworkProviderWorker.java b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
index 57f17b5..698e779 100644
--- a/src/com/android/settings/network/telephony/NetworkProviderWorker.java
+++ b/src/com/android/settings/network/telephony/NetworkProviderWorker.java
@@ -288,9 +288,9 @@
}
/**
- * Returns true, if the ethernet network is connected.
+ * Returns the internet type.
*/
- public boolean isEthernetConnected() {
- return mInternetType == INTERNET_ETHERNET;
+ public @InternetUpdater.InternetType int getInternetType() {
+ return mInternetType;
}
}
diff --git a/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
index 8be6016..d4ad994 100644
--- a/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
+++ b/src/com/android/settings/notification/app/RecentConversationsPreferenceController.java
@@ -72,7 +72,7 @@
return true;
}
- Preference getClearAll(PreferenceGroup parent) {
+ LayoutPreference getClearAll(PreferenceGroup parent) {
LayoutPreference pref = new LayoutPreference(
mContext, R.layout.conversations_clear_recents);
pref.setOrder(1);
@@ -80,10 +80,21 @@
button.setOnClickListener(v -> {
try {
mPs.removeAllRecentConversations();
+ // Removing recents is asynchronous, so we can't immediately reload the list from
+ // the backend. Instead, proactively remove all of items that were marked as
+ // clearable, so long as we didn't get an error
+
+ for (int i = parent.getPreferenceCount() - 1; i >= 0; i--) {
+ Preference p = parent.getPreference(i);
+ if (p instanceof RecentConversationPreference) {
+ if (((RecentConversationPreference) p).hasClearListener()) {
+ parent.removePreference(p);
+ }
+ }
+ }
} catch (RemoteException e) {
Slog.w(TAG, "Could not clear recents", e);
}
- updateState(parent);
});
return pref;
}
@@ -118,36 +129,45 @@
protected void populateList(List<ConversationChannel> conversations,
PreferenceGroup containerGroup) {
containerGroup.removeAll();
+ boolean hasClearable = false;
if (conversations != null) {
- populateConversations(conversations, containerGroup);
+ hasClearable = populateConversations(conversations, containerGroup);
}
if (containerGroup.getPreferenceCount() == 0) {
containerGroup.setVisible(false);
} else {
containerGroup.setVisible(true);
- Preference clearAll = getClearAll(containerGroup);
- if (clearAll != null) {
- containerGroup.addPreference(clearAll);
+ if (hasClearable) {
+ Preference clearAll = getClearAll(containerGroup);
+ if (clearAll != null) {
+ containerGroup.addPreference(clearAll);
+ }
}
}
}
- protected void populateConversations(List<ConversationChannel> conversations,
+ protected boolean populateConversations(List<ConversationChannel> conversations,
PreferenceGroup containerGroup) {
int order = 100;
+ boolean hasClearable = false;
for (ConversationChannel conversation : conversations) {
if (conversation.getParentNotificationChannel().getImportance() == IMPORTANCE_NONE
|| (conversation.getParentNotificationChannelGroup() != null
&& conversation.getParentNotificationChannelGroup().isBlocked())) {
continue;
}
- containerGroup.addPreference(
- createConversationPref(containerGroup, conversation, order++));
+ RecentConversationPreference pref =
+ createConversationPref(containerGroup, conversation, order++);
+ containerGroup.addPreference(pref);
+ if (pref.hasClearListener()) {
+ hasClearable = true;
+ }
}
+ return hasClearable;
}
- protected Preference createConversationPref(PreferenceGroup parent,
+ protected RecentConversationPreference createConversationPref(PreferenceGroup parent,
final ConversationChannel conversation, int order) {
final String pkg = conversation.getShortcutInfo().getPackage();
final int uid = conversation.getUid();
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 42d98e0..e7b23b9 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -32,6 +32,7 @@
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -40,6 +41,7 @@
import com.android.settings.panel.PanelFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
+import com.android.settings.security.SecuritySettingsFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
import com.android.settings.users.UserFeatureProvider;
import com.android.settings.wifi.WifiTrackerLibProvider;
@@ -112,6 +114,12 @@
public abstract BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(
Context context);
+ /**
+ * Get implementation for Battery Settings provider.
+ */
+ public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(
+ Context context);
+
public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
public abstract DockUpdaterFeatureProvider getDockUpdaterFeatureProvider();
@@ -155,6 +163,11 @@
*/
public abstract ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider();
+ /**
+ * Retrieve implementation for SecuritySettings feature.
+ */
+ public abstract SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider();
+
public static final class FactoryNotFoundException extends RuntimeException {
public FactoryNotFoundException(Throwable throwable) {
super("Unable to create factory. Did you misconfigure Proguard?", throwable);
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 46b263a..00f9a0e 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -45,6 +45,8 @@
import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProviderImpl;
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
import com.android.settings.fuelgauge.BatteryStatusFeatureProviderImpl;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -61,6 +63,8 @@
import com.android.settings.search.SearchFeatureProviderImpl;
import com.android.settings.security.SecurityFeatureProvider;
import com.android.settings.security.SecurityFeatureProviderImpl;
+import com.android.settings.security.SecuritySettingsFeatureProvider;
+import com.android.settings.security.SecuritySettingsFeatureProviderImpl;
import com.android.settings.slices.SlicesFeatureProvider;
import com.android.settings.slices.SlicesFeatureProviderImpl;
import com.android.settings.users.UserFeatureProvider;
@@ -86,6 +90,7 @@
private SuggestionFeatureProvider mSuggestionFeatureProvider;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
+ private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
private UserFeatureProvider mUserFeatureProvider;
private SlicesFeatureProvider mSlicesFeatureProvider;
@@ -97,6 +102,7 @@
private FaceFeatureProvider mFaceFeatureProvider;
private WifiTrackerLibProvider mWifiTrackerLibProvider;
private ExtraAppInfoFeatureProvider mExtraAppInfoFeatureProvider;
+ private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider;
@Override
public SupportFeatureProvider getSupportFeatureProvider(Context context) {
@@ -130,6 +136,14 @@
}
@Override
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ if (mBatterySettingsFeatureProvider == null) {
+ mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl(context);
+ }
+ return mBatterySettingsFeatureProvider;
+ }
+
+ @Override
public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
if (mDashboardFeatureProvider == null) {
mDashboardFeatureProvider = new DashboardFeatureProviderImpl(
@@ -302,4 +316,12 @@
}
return mExtraAppInfoFeatureProvider;
}
+
+ @Override
+ public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() {
+ if (mSecuritySettingsFeatureProvider == null) {
+ mSecuritySettingsFeatureProvider = new SecuritySettingsFeatureProviderImpl();
+ }
+ return mSecuritySettingsFeatureProvider;
+ }
}
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index c52b410..4b9883a 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -32,6 +32,7 @@
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ScrollView;
@@ -508,6 +509,12 @@
Bundle savedInstanceState) {
final GlifLayout layout = (GlifLayout) inflater.inflate(
R.layout.choose_lock_pattern, container, false);
+ switch(getContext().getDisplay().getRotation()) {
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ layout.setLandscapeHeaderAreaVisible(false /* visible */);
+ break;
+ }
layout.setHeaderText(getActivity().getTitle());
if (getResources().getBoolean(R.bool.config_lock_pattern_minimal_ui)) {
View iconView = layout.findViewById(R.id.sud_layout_icon);
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
index 6a97dcd..14b364c 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialActivity.java
@@ -228,7 +228,7 @@
launchedCDC = builder.setHeader(mTitle) // Show the title in the header location
.setDescription(mDetails)
.setAlternateButton(alternateButton)
- .setExternal(true)
+ .setExternal(false)
.setUserId(LockPatternUtils.USER_FRP)
.show();
} else if (isManagedProfile && isInternalActivity()
diff --git a/src/com/android/settings/password/ConfirmLockPattern.java b/src/com/android/settings/password/ConfirmLockPattern.java
index 598e09a..0e18436 100644
--- a/src/com/android/settings/password/ConfirmLockPattern.java
+++ b/src/com/android/settings/password/ConfirmLockPattern.java
@@ -28,6 +28,7 @@
import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -45,6 +46,8 @@
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
+import com.google.android.setupdesign.GlifLayout;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -90,8 +93,7 @@
private boolean mDisappearing = false;
private CountDownTimer mCountdownTimer;
- private TextView mHeaderTextView;
- private TextView mDetailsTextView;
+ private GlifLayout mGlifLayout;
// caller-supplied text for various prompts
private CharSequence mHeaderText;
@@ -117,9 +119,14 @@
: R.layout.confirm_lock_pattern,
container,
false);
- mHeaderTextView = (TextView) view.findViewById(R.id.headerText);
+ mGlifLayout = view.findViewById(R.id.setup_wizard_layout);
+ switch(getContext().getDisplay().getRotation()) {
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ mGlifLayout.setLandscapeHeaderAreaVisible(false /* visible */);
+ break;
+ }
mLockPatternView = (LockPatternView) view.findViewById(R.id.lockPattern);
- mDetailsTextView = (TextView) view.findViewById(R.id.sud_layout_description);
mErrorTextView = (TextView) view.findViewById(R.id.errorText);
mIsManagedProfile = UserManager.get(getActivity()).isManagedProfile(mEffectiveUserId);
@@ -177,7 +184,7 @@
return (float)(numRows - row) / numRows;
}
});
- setAccessibilityTitle(mHeaderTextView.getText());
+ setAccessibilityTitle(mGlifLayout.getHeaderText());
mCredentialCheckResultTracker = (CredentialCheckResultTracker) getFragmentManager()
.findFragmentByTag(FRAGMENT_TAG_CHECK_LOCK_RESULT);
@@ -242,13 +249,13 @@
@Override
public void prepareEnterAnimation() {
super.prepareEnterAnimation();
- mHeaderTextView.setAlpha(0f);
+ mGlifLayout.getHeaderTextView().setAlpha(0f);
mCancelButton.setAlpha(0f);
if (mForgotButton != null) {
mForgotButton.setAlpha(0f);
}
mLockPatternView.setAlpha(0f);
- mDetailsTextView.setAlpha(0f);
+ mGlifLayout.getDescriptionTextView().setAlpha(0f);
}
private int getDefaultDetails() {
@@ -269,8 +276,9 @@
private Object[][] getActiveViews() {
ArrayList<ArrayList<Object>> result = new ArrayList<>();
- result.add(new ArrayList<>(Collections.singletonList(mHeaderTextView)));
- result.add(new ArrayList<>(Collections.singletonList(mDetailsTextView)));
+ result.add(new ArrayList<>(Collections.singletonList(mGlifLayout.getHeaderTextView())));
+ result.add(new ArrayList<>(
+ Collections.singletonList(mGlifLayout.getDescriptionTextView())));
if (mCancelButton.getVisibility() == View.VISIBLE) {
result.add(new ArrayList<>(Collections.singletonList(mCancelButton)));
}
@@ -306,14 +314,14 @@
switch (stage) {
case NeedToUnlock:
if (mHeaderText != null) {
- mHeaderTextView.setText(mHeaderText);
+ mGlifLayout.setHeaderText(mHeaderText);
} else {
- mHeaderTextView.setText(getDefaultHeader());
+ mGlifLayout.setHeaderText(getDefaultHeader());
}
if (mDetailsText != null) {
- mDetailsTextView.setText(mDetailsText);
+ mGlifLayout.setDescriptionText(mDetailsText);
} else {
- mDetailsTextView.setText(getDefaultDetails());
+ mGlifLayout.setDescriptionText(getDefaultDetails());
}
mErrorTextView.setText("");
updateErrorMessage(
@@ -341,7 +349,7 @@
// Always announce the header for accessibility. This is a no-op
// when accessibility is disabled.
- mHeaderTextView.announceForAccessibility(mHeaderTextView.getText());
+ mGlifLayout.getHeaderTextView().announceForAccessibility(mGlifLayout.getHeaderText());
}
private int getDefaultHeader() {
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/CredentialManagementAppAdapter.java b/src/com/android/settings/security/CredentialManagementAppAdapter.java
index e56fc63..6b37f7f 100644
--- a/src/com/android/settings/security/CredentialManagementAppAdapter.java
+++ b/src/com/android/settings/security/CredentialManagementAppAdapter.java
@@ -25,6 +25,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -61,6 +62,7 @@
private final boolean mIncludeHeader;
private final boolean mIncludeExpander;
+ private final boolean mIsLayoutRtl;
/**
* View holder for the header in the request manage credentials screen.
@@ -113,6 +115,15 @@
mChildRecyclerView = view.findViewById(R.id.uris);
mExpandedApps = new ArrayList<>();
+ if (mIsLayoutRtl) {
+ RelativeLayout appDetails = view.findViewById(R.id.app_details);
+ RelativeLayout.LayoutParams params =
+ (RelativeLayout.LayoutParams) appDetails.getLayoutParams();
+ params.addRule(RelativeLayout.LEFT_OF, R.id.app_icon);
+ params.addRule(RelativeLayout.RIGHT_OF, R.id.expand);
+ view.setLayoutParams(params);
+ }
+
mExpanderIconView.setOnClickListener(view1 -> {
final String appName = mSortedAppNames.get(getBindingAdapterPosition());
if (mExpandedApps.contains(appName)) {
@@ -195,6 +206,8 @@
mViewPool = new RecyclerView.RecycledViewPool();
mIncludeHeader = includeHeader;
mIncludeExpander = includeExpander;
+ mIsLayoutRtl = context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
}
/**
diff --git a/src/com/android/settings/security/SecuritySettingsFeatureProvider.java b/src/com/android/settings/security/SecuritySettingsFeatureProvider.java
new file mode 100644
index 0000000..78e4bc7
--- /dev/null
+++ b/src/com/android/settings/security/SecuritySettingsFeatureProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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.security;
+
+/** FeatureProvider for security settings. */
+public interface SecuritySettingsFeatureProvider {
+
+ /** Returns whether an alternative SecuritySettings fragment is available. */
+ boolean hasAlternativeSecuritySettingsFragment();
+
+ /** Returns the alternative SecuritySettings fragment name if available. */
+ String getAlternativeSecuritySettingsFragmentClassname();
+}
diff --git a/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java b/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java
new file mode 100644
index 0000000..8aba523
--- /dev/null
+++ b/src/com/android/settings/security/SecuritySettingsFeatureProviderImpl.java
@@ -0,0 +1,31 @@
+/*
+ * 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.security;
+
+/** Implementation for {@code SecuritySettingsFeatureProvider}. */
+public class SecuritySettingsFeatureProviderImpl implements SecuritySettingsFeatureProvider {
+
+ @Override
+ public boolean hasAlternativeSecuritySettingsFragment() {
+ return false;
+ }
+
+ @Override
+ public String getAlternativeSecuritySettingsFragmentClassname() {
+ return null;
+ }
+}
diff --git a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
index 4efc620..349a91d 100644
--- a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
+++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
@@ -19,12 +19,17 @@
import android.content.Context;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
+import android.text.TextUtils;
import android.util.FeatureFlagUtils;
+import androidx.preference.Preference;
+
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.overlay.FeatureFactory;
public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController {
@@ -56,4 +61,28 @@
return mContext.getText(R.string.security_dashboard_summary_no_fingerprint);
}
}
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return super.handlePreferenceTreeClick(preference);
+ }
+
+ SecuritySettingsFeatureProvider securitySettingsFeatureProvider =
+ FeatureFactory.getFactory(mContext).getSecuritySettingsFeatureProvider();
+ if (securitySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment()) {
+ String alternativeFragmentClassname =
+ securitySettingsFeatureProvider
+ .getAlternativeSecuritySettingsFragmentClassname();
+ if (alternativeFragmentClassname != null) {
+ new SubSettingLauncher(mContext)
+ .setDestination(alternativeFragmentClassname)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ return true;
+ }
+ }
+
+ return super.handlePreferenceTreeClick(preference);
+ }
}
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 53d9849..a9ddf95 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -290,6 +290,9 @@
@VisibleForTesting
void switchUser() {
try {
+ if (mUserInfo.isGuest()) {
+ mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_SWITCH_TO_GUEST);
+ }
ActivityManager.getService().switchUser(mUserInfo.id);
} catch (RemoteException re) {
Log.e(TAG, "Error while switching to other user.");
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 8010f41..6e82aab 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -820,6 +820,8 @@
*/
private void exitGuest() {
// Just to be safe
+ mMetricsFeatureProvider.action(getActivity(),
+ SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
if (!isCurrentUserGuest()) {
return;
}
@@ -1077,6 +1079,7 @@
return true;
} else if (pref == mAddGuest) {
mAddGuest.setEnabled(false); // prevent multiple tap issue
+ mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_GUEST_ADD);
UserInfo guest = mUserManager.createGuest(
getContext(), getString(com.android.settingslib.R.string.user_guest));
openUserDetails(guest, true);
diff --git a/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java b/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
index e8eab68..dee5817 100644
--- a/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
+++ b/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
@@ -16,8 +16,6 @@
package com.android.settings.utils;
-import android.os.Process;
-import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
import android.util.Log;
@@ -41,6 +39,8 @@
public class AndroidKeystoreAliasLoader {
private static final String TAG = "SettingsKeystoreUtils";
+ private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+
private final Collection<String> mKeyCertAliases;
private final Collection<String> mCaCertAliases;
/**
@@ -58,21 +58,13 @@
public AndroidKeystoreAliasLoader(Integer namespace) {
mKeyCertAliases = new ArrayList<>();
mCaCertAliases = new ArrayList<>();
- KeyStore keyStore = null;
+ final KeyStore keyStore;
final Enumeration<String> aliases;
try {
+ keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
if (namespace != null && namespace != KeyProperties.NAMESPACE_APPLICATION) {
- if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
- keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(new AndroidKeyStoreLoadStoreParameter(namespace));
- } else {
- // In the legacy case we pass in the WIFI UID because that is the only
- // possible special namespace that existed as of this writing,
- // and new namespaces must only be added using the new mechanism.
- keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
- }
+ keyStore.load(new AndroidKeyStoreLoadStoreParameter(namespace));
} else {
- keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
}
aliases = keyStore.aliases();
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index 03e3613..24fb576 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -204,10 +204,16 @@
configureAdvancedOptionsVisibility();
- // Create a button to forget the profile if it has already been saved..
if (mExists) {
+ // Create a button to forget the profile if it has already been saved..
setButton(DialogInterface.BUTTON_NEUTRAL,
context.getString(R.string.vpn_forget), mListener);
+
+ // Display warning subtitle if the existing VPN is an insecure type...
+ if (VpnProfile.isLegacyType(mProfile.type)) {
+ TextView subtitle = mView.findViewById(R.id.dialog_alert_subtitle);
+ subtitle.setVisibility(View.VISIBLE);
+ }
}
// Create a button to save the profile.
diff --git a/src/com/android/settings/vpn2/ManageablePreference.java b/src/com/android/settings/vpn2/ManageablePreference.java
index 68971f2..c65073f 100644
--- a/src/com/android/settings/vpn2/ManageablePreference.java
+++ b/src/com/android/settings/vpn2/ManageablePreference.java
@@ -16,15 +16,20 @@
package com.android.settings.vpn2;
+import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE;
+
import android.content.Context;
import android.content.res.Resources;
import android.os.UserHandle;
import android.os.UserManager;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import com.android.settings.R;
import com.android.settings.widget.GearPreference;
+import com.android.settingslib.Utils;
/**
* This class sets appropriate enabled state and user admin message when userId is set
@@ -34,6 +39,7 @@
public static int STATE_NONE = -1;
boolean mIsAlwaysOn = false;
+ boolean mIsInsecureVpn = false;
int mState = STATE_NONE;
int mUserId;
@@ -57,6 +63,10 @@
return mIsAlwaysOn;
}
+ public boolean isInsecureVpn() {
+ return mIsInsecureVpn;
+ }
+
public int getState() {
return mState;
}
@@ -77,8 +87,19 @@
}
/**
+ * Set whether the VPN associated with this preference has an insecure type.
+ * By default the value will be False.
+ */
+ public void setInsecureVpn(boolean isInsecureVpn) {
+ if (mIsInsecureVpn != isInsecureVpn) {
+ mIsInsecureVpn = isInsecureVpn;
+ updateSummary();
+ }
+ }
+
+ /**
* Update the preference summary string (see {@see Preference#setSummary}) with a string
- * reflecting connection status and always-on setting.
+ * reflecting connection status, always-on setting and whether the vpn is insecure.
*
* State is not shown for {@code STATE_NONE}.
*/
@@ -91,6 +112,18 @@
summary = TextUtils.isEmpty(summary) ? alwaysOnString : res.getString(
R.string.join_two_unrelated_items, summary, alwaysOnString);
}
- setSummary(summary);
+ if (mIsInsecureVpn) {
+ final String insecureString = res.getString(R.string.vpn_insecure_summary);
+ summary = TextUtils.isEmpty(summary) ? insecureString : res.getString(
+ R.string.join_two_unrelated_items, summary, insecureString);
+
+ SpannableString summarySpan = new SpannableString(summary);
+ final int colorError = Utils.getColorErrorDefaultColor(getContext());
+ summarySpan.setSpan(new ForegroundColorSpan(colorError), 0, summary.length(),
+ SPAN_EXCLUSIVE_INCLUSIVE);
+ setSummary(summarySpan);
+ } else {
+ setSummary(summary);
+ }
}
}
diff --git a/src/com/android/settings/vpn2/VpnInfoPreference.java b/src/com/android/settings/vpn2/VpnInfoPreference.java
new file mode 100644
index 0000000..cca45b5
--- /dev/null
+++ b/src/com/android/settings/vpn2/VpnInfoPreference.java
@@ -0,0 +1,99 @@
+/*
+ * 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.vpn2;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.preference.PreferenceViewHolder;
+
+import com.android.settings.R;
+import com.android.settingslib.HelpUtils;
+import com.android.settingslib.RestrictedPreference;
+
+
+/**
+ * A preference with an Info icon on the side
+ */
+public class VpnInfoPreference extends RestrictedPreference implements View.OnClickListener {
+
+ private boolean mIsInsecureVpn = false;
+ private String mHelpUrl;
+
+ public VpnInfoPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mHelpUrl = context.getString(R.string.help_url_insecure_vpn);
+ }
+
+ @Override
+ protected int getSecondTargetResId() {
+ // Note: in the future, we will probably want to provide a configuration option
+ // for this info to not be the warning color.
+ return R.layout.preference_widget_warning;
+ }
+
+ @Override
+ protected boolean shouldHideSecondTarget() {
+ return false;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ final View icon = holder.findViewById(R.id.warning_button);
+ if (mIsInsecureVpn && !TextUtils.isEmpty(mHelpUrl)) {
+ icon.setVisibility(View.VISIBLE);
+ icon.setOnClickListener(this);
+ icon.setEnabled(true);
+ } else {
+ icon.setVisibility(View.GONE);
+ icon.setOnClickListener(this);
+ icon.setEnabled(false);
+ }
+
+ // Hide the divider from view
+ final View divider = holder.findViewById(R.id.two_target_divider);
+ divider.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.warning_button) {
+ final Intent intent = HelpUtils.getHelpIntent(
+ getContext(), mHelpUrl, this.getClass().getName());
+
+ if (intent != null) {
+ ((Activity) getContext()).startActivityForResult(intent, 0);
+ }
+ }
+ }
+
+ /**
+ * Sets whether this preference corresponds to an insecure VPN. This will also affect whether
+ * the warning icon appears to the user.
+ */
+ public void setInsecureVpn(boolean isInsecureVpn) {
+ if (mIsInsecureVpn != isInsecureVpn) {
+ mIsInsecureVpn = isInsecureVpn;
+ notifyChanged();
+ }
+ }
+}
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index 7635733..cdc6537 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -294,6 +294,7 @@
p.setState(LegacyVpnPreference.STATE_NONE);
}
p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(profile.key));
+ p.setInsecureVpn(VpnProfile.isLegacyType(profile.type));
updates.add(p);
}
@@ -303,6 +304,7 @@
LegacyVpnPreference p = mSettings.findOrCreatePreference(stubProfile, false);
p.setState(vpn.state);
p.setAlwaysOn(lockdownVpnKey != null && lockdownVpnKey.equals(vpn.key));
+ p.setInsecureVpn(VpnProfile.isLegacyType(stubProfile.type));
updates.add(p);
}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 5545de6..97a74d7 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -63,7 +63,7 @@
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.datausage.DataUsagePreference;
import com.android.settings.datausage.DataUsageUtils;
-import com.android.settings.location.LocationServices;
+import com.android.settings.location.WifiScanningFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.MainSwitchBarController;
@@ -921,8 +921,8 @@
: getText(R.string.wifi_scan_notify_text_scanning_off);
final LinkifyUtils.OnClickListener clickListener =
() -> new SubSettingLauncher(getContext())
- .setDestination(LocationServices.class.getName())
- .setTitleRes(R.string.location_services_screen_title)
+ .setDestination(WifiScanningFragment.class.getName())
+ .setTitleRes(R.string.location_scanning_wifi_always_scanning_title)
.setSourceMetricsCategory(getMetricsCategory())
.launch();
mStatusMessagePreference.setText(title, description, clickListener);
diff --git a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
index 5869705..e9fd350 100644
--- a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
@@ -26,7 +26,6 @@
import android.location.LocationManager;
import android.net.wifi.WifiManager;
import android.provider.Settings;
-import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
@@ -45,7 +44,6 @@
* {@link TogglePreferenceController} that controls whether the Wi-Fi Wakeup feature should be
* enabled.
*/
-// TODO(b/167474581): Should clean up this controller when Provider Model finished.
public class WifiWakeupPreferenceController extends TogglePreferenceController implements
LifecycleObserver, OnPause, OnResume {
@@ -91,9 +89,6 @@
@Override
public int getAvailabilityStatus() {
- if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
- return CONDITIONALLY_UNAVAILABLE;
- }
return AVAILABLE;
}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index 211ceed..0c3f42d 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -42,6 +42,7 @@
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.Uri;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -155,6 +156,8 @@
static final String KEY_IPV6_CATEGORY = "ipv6_category";
@VisibleForTesting
static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses";
+ @VisibleForTesting
+ static final String KEY_WIFI_TYPE_PREF = "type";
private final WifiEntry mWifiEntry;
private final ConnectivityManager mConnectivityManager;
@@ -186,6 +189,7 @@
private Preference mGatewayPref;
private Preference mSubnetPref;
private Preference mDnsPref;
+ private Preference mTypePref;
private PreferenceCategory mIpv6Category;
private Preference mIpv6AddressPref;
private Lifecycle mLifecycle;
@@ -382,6 +386,7 @@
mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF);
mSubnetPref = screen.findPreference(KEY_SUBNET_MASK_PREF);
mDnsPref = screen.findPreference(KEY_DNS_PREF);
+ mTypePref = screen.findPreference(KEY_WIFI_TYPE_PREF);
mIpv6Category = screen.findPreference(KEY_IPV6_CATEGORY);
mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
@@ -552,6 +557,8 @@
refreshEapSimSubscription();
// MAC Address Pref
refreshMacAddress();
+ // Wifi Type
+ refreshWifiType();
}
private void refreshRssiViews() {
@@ -749,6 +756,36 @@
}
}
+ private void refreshWifiType() {
+ final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo();
+ if (connectedInfo == null) {
+ mTypePref.setVisible(false);
+ return;
+ }
+
+ final int typeString = getWifiStandardTypeString(connectedInfo.wifiStandard);
+ if (typeString != -1) {
+ mTypePref.setSummary(typeString);
+ mTypePref.setVisible(true);
+ } else {
+ mTypePref.setVisible(false);
+ }
+ }
+
+ private int getWifiStandardTypeString(int wifiStandardType) {
+ Log.d(TAG, "Wifi Type " + wifiStandardType);
+ switch (wifiStandardType) {
+ case ScanResult.WIFI_STANDARD_11AX:
+ return R.string.wifi_type_11AX;
+ case ScanResult.WIFI_STANDARD_11AC:
+ return R.string.wifi_type_11AC;
+ case ScanResult.WIFI_STANDARD_11N:
+ return R.string.wifi_type_11N;
+ default:
+ return -1;
+ }
+ }
+
private int getMacAddressTitle() {
if (mWifiEntry.getPrivacy() == WifiEntry.PRIVACY_RANDOMIZED_MAC) {
return mWifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED
diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
index b154a9b..104761f 100644
--- a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
+++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
@@ -75,7 +75,8 @@
// Interval between initiating SavedNetworkTracker scans
private static final long SCAN_INTERVAL_MILLIS = 10_000;
- private NetworkDetailsTracker mNetworkDetailsTracker;
+ @VisibleForTesting
+ NetworkDetailsTracker mNetworkDetailsTracker;
private HandlerThread mWorkerThread;
private WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
private List<WifiDialog2.WifiDialog2Listener> mWifiDialogListeners = new ArrayList<>();
@@ -125,9 +126,11 @@
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
- item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
- item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ if (isEditable()) {
+ MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
+ item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
super.onCreateOptionsMenu(menu, inflater);
}
@@ -252,6 +255,17 @@
getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
}
+ private boolean isEditable() {
+ if (mNetworkDetailsTracker == null) {
+ return false;
+ }
+ final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
+ if (wifiEntry == null) {
+ return false;
+ }
+ return wifiEntry.isSaved();
+ }
+
/**
* API call for refreshing the preferences in this fragment.
*/
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 898de56..e4a571d 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -164,7 +164,7 @@
return rowBuilder;
}
- private IconCompat getWifiSliceItemLevelIcon(WifiSliceItem wifiSliceItem) {
+ protected IconCompat getWifiSliceItemLevelIcon(WifiSliceItem wifiSliceItem) {
final @ColorInt int tint;
if (wifiSliceItem.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
tint = Utils.getColorAccentDefaultColor(mContext);
diff --git a/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java
index 46da097..a950dc3 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherBasePreferenceController.java
@@ -45,8 +45,8 @@
OnTetherConfigUpdateListener listener) {
super(context);
mListener = listener;
- mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mTm = (TetheringManager) context.getSystemService(Context.TETHERING_SERVICE);
+ mWifiManager = context.getSystemService(WifiManager.class);
+ mTm = context.getSystemService(TetheringManager.class);
mWifiRegexs = mTm.getTetherableWifiRegexs();
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
index bc87d5c..41532d0 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceController.java
@@ -86,7 +86,11 @@
if (mWifiManager.isBridgedApConcurrencySupported()) {
final boolean isEnabled = config.isBridgedModeOpportunisticShutdownEnabled();
Log.d(TAG, "isBridgedModeOpportunisticShutdownEnabled:" + isEnabled);
- return isEnabled;
+ // Because the return value defined by the Wi-Fi framework API is opposite to the UI.
+ // Compatibility on: isBridgedModeOpportunisticShutdownEnabled() = false
+ // Compatibility off: isBridgedModeOpportunisticShutdownEnabled() = true
+ // Need to return the reverse value.
+ return !isEnabled;
}
// If the BridgedAp Concurrency is not supported in early Pixel devices (e.g. Pixel 2~5),
@@ -112,7 +116,11 @@
SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ};
builder.setBands(bands);
Log.d(TAG, "setBridgedModeOpportunisticShutdownEnabled:" + enabled);
- builder.setBridgedModeOpportunisticShutdownEnabled(enabled);
+ // Because the defined value by the Wi-Fi framework API is opposite to the UI.
+ // Compatibility on: setBridgedModeOpportunisticShutdownEnabled(false)
+ // Compatibility off: setBridgedModeOpportunisticShutdownEnabled(true)
+ // Need to set the reverse value.
+ builder.setBridgedModeOpportunisticShutdownEnabled(!enabled);
} else {
int band = enabled
? SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ
diff --git a/tests/anomaly-tester/AndroidManifest.xml b/tests/anomaly-tester/AndroidManifest.xml
index d6f68a8..3c5fb1f 100644
--- a/tests/anomaly-tester/AndroidManifest.xml
+++ b/tests/anomaly-tester/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
@@ -49,4 +51,4 @@
android:label="Settings Test Cases">
</instrumentation>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/componenttests/AndroidManifest.xml b/tests/componenttests/AndroidManifest.xml
index 54ea374..fb6c26f 100644
--- a/tests/componenttests/AndroidManifest.xml
+++ b/tests/componenttests/AndroidManifest.xml
@@ -20,6 +20,8 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@ -37,4 +39,4 @@
android:label="Settings Test Cases">
</instrumentation>
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/robotests/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
index 39a663e..01942f0 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ReduceBrightColorsPreferenceControllerTest.java
@@ -19,7 +19,6 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
-import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -32,29 +31,14 @@
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsPreferenceControllerTest {
private static final String PREF_KEY = "rbc_preference";
- private static final String RBC_ACTIVATED =
- Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED;
- private static final int ON = 1;
- private static final int OFF = 0;
private final Context mContext = ApplicationProvider.getApplicationContext();
private final ReduceBrightColorsPreferenceController mController =
new ReduceBrightColorsPreferenceController(mContext, PREF_KEY);
@Test
- public void getSummary_enabledRbc_shouldReturnOnSummary() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- RBC_ACTIVATED, ON);
-
+ public void getSummary_returnSummary() {
assertThat(mController.getSummary().toString().contains(
- mContext.getText(R.string.accessibility_feature_state_on))).isTrue();
- }
- @Test
- public void getSummary_disabledRbc_shouldReturnOffSummary() {
- Settings.Secure.putInt(mContext.getContentResolver(),
- RBC_ACTIVATED, OFF);
-
- assertThat(mController.getSummary().toString().contains(
- mContext.getText(R.string.accessibility_feature_state_off))).isTrue();
+ mContext.getText(R.string.reduce_bright_colors_preference_summary))).isTrue();
}
}
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
index b22b156..4b6a6a8 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountPreferenceControllerTest.java
@@ -39,6 +39,7 @@
import android.text.TextUtils;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
@@ -72,9 +73,13 @@
ShadowSettingsLibUtils.class})
public class AccountPreferenceControllerTest {
+ private static final String PREF_KEY_ACCOUNTS = "accounts_category";
+
@Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
@Mock(answer = RETURNS_DEEP_STUBS)
+ private PreferenceCategory mAccountsCategory;
+ @Mock(answer = RETURNS_DEEP_STUBS)
private UserManager mUserManager;
@Mock(answer = RETURNS_DEEP_STUBS)
private SettingsPreferenceFragment mFragment;
@@ -95,6 +100,9 @@
shadowApp.setSystemService(Context.ACCOUNT_SERVICE, mAccountManager);
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
+ // This is a bit ugly, but hard to avoid because of how the mocks are used in these tests.
+ // TODO: Refactor these tests to not use RETURNS_DEEP_STUBS.
+ when(mScreen.findPreference(PREF_KEY_ACCOUNTS)).thenReturn(mScreen);
when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
when(mAccountManager.getAuthenticatorTypesAsUser(anyInt()))
.thenReturn(new AuthenticatorDescription[0]);
@@ -166,6 +174,7 @@
// First time resume will build the UI
mController.onResume();
reset(mScreen);
+ when(mScreen.findPreference(PREF_KEY_ACCOUNTS)).thenReturn(mScreen);
mController.onResume();
verify(mScreen, never()).addPreference(any(PreferenceGroup.class));
@@ -184,6 +193,7 @@
// add a new profile
infos.add(new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE));
reset(mScreen);
+ when(mScreen.findPreference(PREF_KEY_ACCOUNTS)).thenReturn(mScreen);
mController.onResume();
verify(mScreen, times(1)).addPreference(any(PreferenceGroup.class));
diff --git a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
index 534d3c6..04db527 100644
--- a/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AvatarViewMixinTest.java
@@ -27,7 +27,6 @@
import static org.mockito.Mockito.verify;
import android.accounts.Account;
-import android.app.ActivityManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -57,7 +56,6 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowActivityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowPackageManager;
@@ -97,31 +95,7 @@
}
@Test
- public void onStart_configDisabled_doNothing() {
- final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
- mixin.onStart();
-
- verify(mixin, never()).hasAccount();
- }
-
- @Test
- public void onStart_lowRamDevice_doNothing() {
- final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
-
- final ShadowActivityManager activityManager =
- Shadow.extract(mContext.getSystemService(ActivityManager.class));
- activityManager.setIsLowRamDevice(true);
-
- mixin.onStart();
-
- verify(mixin, never()).hasAccount();
- }
-
- @Test
- @Config(qualifiers = "mcc999",
- shadows = {
- BatteryFixSliceTest.ShadowBatteryTipLoader.class
- })
+ @Config(shadows = BatteryFixSliceTest.ShadowBatteryTipLoader.class)
public void onStart_useMockAvatarViewMixin_shouldBeExecuted() {
final AvatarViewMixin mockAvatar = spy(new AvatarViewMixin(mActivity, mImageView));
@@ -132,7 +106,6 @@
}
@Test
- @Config(qualifiers = "mcc999")
public void onStart_noAccount_mAccountNameShouldBeNull() {
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(mActivity, mImageView);
avatarViewMixin.mAccountName = FAKE_ACCOUNT;
diff --git a/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java
index 3a67d7f..1d63cd1 100644
--- a/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/TopLevelAccountEntryPreferenceControllerTest.java
@@ -23,64 +23,33 @@
import com.android.settings.R;
import com.android.settings.core.FeatureFlags;
-import com.android.settings.testutils.shadow.ShadowAuthenticationHelper;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowAuthenticationHelper.class})
public class TopLevelAccountEntryPreferenceControllerTest {
private TopLevelAccountEntryPreferenceController mController;
private Context mContext;
- private String[] LABELS;
- private String[] TYPES;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mController = new TopLevelAccountEntryPreferenceController(mContext, "test_key");
- LABELS = ShadowAuthenticationHelper.getLabels();
- TYPES = ShadowAuthenticationHelper.getTypes();
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.SILKY_HOME, false);
}
- @After
- public void tearDown() {
- ShadowAuthenticationHelper.reset();
- }
-
- @Test
- public void updateSummary_hasAccount_shouldDisplayUpTo3AccountTypes() {
- assertThat(mController.getSummary())
- .isEqualTo(LABELS[0] + ", " + LABELS[1] + ", and " + LABELS[2]);
- }
-
@Test
public void updateSummary_noAccount_shouldDisplayDefaultSummary() {
- ShadowAuthenticationHelper.setEnabledAccount(null);
-
assertThat(mController.getSummary()).isEqualTo(
mContext.getText(R.string.account_dashboard_default_summary));
}
@Test
- public void updateSummary_noAccountTypeLabel_shouldNotDisplayNullEntry() {
- final String[] enabledAccounts = {TYPES[0], "unlabeled_account_type", TYPES[1]};
- ShadowAuthenticationHelper.setEnabledAccount(enabledAccounts);
-
-
- // should only show the 2 accounts with labels
- assertThat(mController.getSummary()).isEqualTo(LABELS[0] + " and " + LABELS[1]);
- }
-
- @Test
public void getSummary_silkyHomeEnabled_shouldBeNull() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.SILKY_HOME, true);
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
index 849f2ac..1073256 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppOpenByDefaultPreferenceControllerTest.java
@@ -37,7 +37,7 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import com.android.settings.applications.AppLaunchSettings;
+import com.android.settings.applications.intentpicker.AppLaunchSettings;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.instantapps.InstantAppDataProvider;
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
index c8891e5..8e03d68 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
@@ -62,6 +62,9 @@
private static final int BATTERY_LEVEL_RIGHT = 45;
private static final int LOW_BATTERY_LEVEL = 15;
private static final int CASE_LOW_BATTERY_LEVEL = 19;
+ private static final int LOW_BATTERY_LEVEL_THRESHOLD = 15;
+ private static final int BATTERY_LEVEL_5 = 5;
+ private static final int BATTERY_LEVEL_50 = 50;
private static final String ICON_URI = "content://test.provider/icon.png";
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
@@ -202,6 +205,70 @@
}
@Test
+ public void refresh_underLowBatteryThreshold_showAlertIcon() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_5).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD)).thenReturn(
+ String.valueOf(LOW_BATTERY_LEVEL_THRESHOLD).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_CHARGING)).thenReturn(
+ String.valueOf(false).getBytes());
+ when(mCachedDevice.isConnected()).thenReturn(true);
+
+ mController.refresh();
+
+ assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle),
+ R.drawable.ic_battery_alert_24dp);
+ }
+
+ @Test
+ public void refresh_underLowBatteryThresholdInCharging_showAlertIcon() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_5).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD)).thenReturn(
+ String.valueOf(LOW_BATTERY_LEVEL_THRESHOLD).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_CHARGING)).thenReturn(
+ String.valueOf(true).getBytes());
+ when(mCachedDevice.isConnected()).thenReturn(true);
+
+ mController.refresh();
+
+ assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle), /* resId= */-1);
+ }
+
+ @Test
+ public void refresh_aboveLowBatteryThreshold_noAlertIcon() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
+ String.valueOf(BATTERY_LEVEL_50).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD)).thenReturn(
+ String.valueOf(LOW_BATTERY_LEVEL_THRESHOLD).getBytes());
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_MAIN_CHARGING)).thenReturn(
+ String.valueOf(false).getBytes());
+ when(mCachedDevice.isConnected()).thenReturn(true);
+
+ mController.refresh();
+
+ assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle), /* resId= */-1);
+ }
+
+ @Test
public void refresh_withLowBatteryAndUncharged_showAlertIcon() {
when(mBluetoothDevice.getMetadata(
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
index 68b2990..1262b50 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
@@ -24,6 +24,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.app.time.Capabilities;
@@ -36,10 +37,13 @@
import android.os.UserHandle;
import com.android.settings.R;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settingslib.core.lifecycle.Lifecycle;
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;
@@ -53,6 +57,10 @@
private LocationManager mLocationManager;
private Context mContext;
private LocationTimeZoneDetectionPreferenceController mController;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private InstrumentedPreferenceFragment mFragment;
+ @Mock
+ private Lifecycle mLifecycle;
@Before
public void setUp() {
@@ -60,11 +68,14 @@
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(TimeManager.class)).thenReturn(mTimeManager);
when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
- mController = new LocationTimeZoneDetectionPreferenceController(mContext, "key");
+ mController = new LocationTimeZoneDetectionPreferenceController(mContext);
+ mController.setFragment(mFragment);
}
@Test
- public void setChecked_withTrue_shouldUpdateSetting() {
+ public void setChecked_withTrue_shouldUpdateSetting_whenLocationIsEnabled() {
+ when(mLocationManager.isLocationEnabled()).thenReturn(true);
+
// Simulate the UI being clicked.
mController.setChecked(true);
@@ -76,6 +87,17 @@
}
@Test
+ public void setChecked_withTrue_shouldDoNothing_whenLocationIsDisabled() {
+ when(mLocationManager.isLocationEnabled()).thenReturn(false);
+
+ // Simulate the UI being clicked.
+ mController.setChecked(true);
+
+ // Verify the TimeManager was not called.
+ verifyZeroInteractions(mTimeManager);
+ }
+
+ @Test
public void setChecked_withFalse_shouldUpdateSetting() {
// Simulate the UI being clicked.
mController.setChecked(false);
diff --git a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
index d21aa04..784b3bb 100644
--- a/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/timezone/model/TimeZoneDataTest.java
@@ -92,4 +92,23 @@
.containsExactly("US", "GB");
assertThat(timeZoneData.lookupCountryCodesForZoneId("Unknown/Secret_City2")).isEmpty();
}
+
+ @Test
+ public void lookupCountryCodesForNonCanonicalZoneId_returnsCurrentZone() {
+ TimeZoneData timeZoneData = new TimeZoneData(mCountryZonesFinder);
+
+ CountryTimeZones greenland = mock(CountryTimeZones.class);
+ when(greenland.getCountryIso()).thenReturn("gl");
+ when(greenland.getTimeZoneMappings()).thenReturn(Arrays.asList(
+ TimeZoneMapping.createForTests(
+ "America/Nuuk",
+ true /* showInPicker */,
+ null /* notUsedAfter */,
+ Arrays.asList("America/Godthab"))));
+ when(mCountryZonesFinder.lookupCountryTimeZonesForZoneId("America/Godthab"))
+ .thenReturn(Arrays.asList(greenland));
+
+ assertThat(timeZoneData.lookupCountryCodesForZoneId("America/Godthab"))
+ .containsExactly("GL");
+ }
}
diff --git a/tests/robotests/src/com/android/settings/development/DefaultUsbConfigurationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/DefaultUsbConfigurationPreferenceControllerTest.java
index 15f4eb9..c9b13e27 100644
--- a/tests/robotests/src/com/android/settings/development/DefaultUsbConfigurationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/DefaultUsbConfigurationPreferenceControllerTest.java
@@ -91,4 +91,27 @@
verify(mPreference).setDisabledByAdmin(eq(new RestrictedLockUtils.EnforcedAdmin(
TEST_COMPONENT_NAME, null, UserHandle.SYSTEM)));
}
+
+ @Test
+ public void onDeveloperOptionsSwitchEnabled_usbEnabled_shouldNotDisablePreference() {
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+
+ mController.onDeveloperOptionsSwitchEnabled();
+
+ verify(mPreference).setDisabledByAdmin(null);
+ }
+
+ @Test
+ public void onDeveloperOptionsSwitchEnabled_usbDisabled_shouldDisablePreference() {
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(false);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+
+ mController.onDeveloperOptionsSwitchEnabled();
+
+ verify(mPreference).setDisabledByAdmin(eq(new RestrictedLockUtils.EnforcedAdmin(
+ TEST_COMPONENT_NAME, null, UserHandle.SYSTEM)));
+ }
}
diff --git a/tests/robotests/src/com/android/settings/development/UsbAudioRoutingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/UsbAudioRoutingPreferenceControllerTest.java
index 1d45c1b..69d2c99 100644
--- a/tests/robotests/src/com/android/settings/development/UsbAudioRoutingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/UsbAudioRoutingPreferenceControllerTest.java
@@ -147,4 +147,27 @@
verify(mPreference).setEnabled(false);
verify(mPreference).setChecked(false);
}
+
+ @Test
+ public void onDeveloperOptionsSwitchEnabled_usbEnabled_shouldNotDisablePreference() {
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+
+ mController.onDeveloperOptionsSwitchEnabled();
+
+ verify(mPreference).setDisabledByAdmin(null);
+ }
+
+ @Test
+ public void onDeveloperOptionsSwitchEnabled_usbDisabled_shouldDisablePreference() {
+ when(mDevicePolicyManager.isUsbDataSignalingEnabledForUser(
+ UserHandle.myUserId())).thenReturn(false);
+ when(mDevicePolicyManager.getProfileOwner()).thenReturn(TEST_COMPONENT_NAME);
+
+ mController.onDeveloperOptionsSwitchEnabled();
+
+ verify(mPreference).setDisabledByAdmin(eq(new RestrictedLockUtils.EnforcedAdmin(
+ TEST_COMPONENT_NAME, null, UserHandle.SYSTEM)));
+ }
}
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/StorageItemPreferenceTest.java b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
index 06194b4..5795157 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/StorageItemPreferenceTest.java
@@ -69,4 +69,11 @@
assertThat(progressBar).isNotNull();
assertThat(progressBar.getProgress()).isEqualTo(10);
}
+
+ @Test
+ public void getStorageSize_setStorageSize_getCorrectStorageSize() {
+ mPreference.setStorageSize(MEGABYTE_IN_BYTES, MEGABYTE_IN_BYTES * 10);
+
+ assertThat(mPreference.getStorageSize()).isEqualTo(MEGABYTE_IN_BYTES);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
index d1371a2..384ddc3 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/AutomaticStorageManagementSwitchPreferenceControllerTest.java
@@ -22,12 +22,14 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -38,6 +40,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.RoSystemProperties;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.deletionhelper.ActivationWarningFragment;
import com.android.settings.overlay.FeatureFactory;
@@ -67,13 +70,16 @@
private FragmentManager mFragmentManager;
private Context mContext;
+ private Resources mResources;
private AutomaticStorageManagementSwitchPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application.getApplicationContext();
+ mContext = spy(RuntimeEnvironment.application.getApplicationContext());
FeatureFactory.getFactory(mContext);
+ mResources = spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
mController = new AutomaticStorageManagementSwitchPreferenceController(mContext, "testkey");
mController.setFragmentManager(mFragmentManager);
@@ -81,14 +87,27 @@
}
@Test
- public void isAvailable_shouldReturnTrue_forHighRamDevice() {
+ public void getAvailabilityStatus_configFalse_shouldUnsupportedOnDevice() {
+ when(mResources.getBoolean(R.bool.config_show_smart_storage_toggle)).thenReturn(false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_forHighRamDevice_shouldAvailable() {
+ when(mResources.getBoolean(R.bool.config_show_smart_storage_toggle)).thenReturn(true);
+
assertThat(mController.isAvailable()).isTrue();
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
- public void isAvailable_shouldAlwaysReturnFalse_forLowRamDevice() {
+ public void getAvailabilityStatus_forLowRamDevice_shouldUnsupportedOnDevice() {
+ when(mResources.getBoolean(R.bool.config_show_smart_storage_toggle)).thenReturn(true);
+
ReflectionHelpers.setStaticField(RoSystemProperties.class, "CONFIG_LOW_RAM", true);
assertThat(mController.isAvailable()).isFalse();
assertThat(mController.getAvailabilityStatus()).isEqualTo(
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 5e49aa0..b7bf7f5 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -43,7 +43,6 @@
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.PreferenceScreen;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
@@ -51,10 +50,8 @@
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.StorageItemPreference;
-import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.applications.StorageStatsSource;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import org.junit.Before;
@@ -84,8 +81,7 @@
private FragmentTransaction mFragmentTransaction;
private StorageItemPreferenceController mController;
private StorageItemPreference mPreference;
- private FakeFeatureFactory mFakeFeatureFactory;
- private MetricsFeatureProvider mMetricsFeatureProvider;
+ private PreferenceScreen mPreferenceScreen;
@Before
public void setUp() {
@@ -94,8 +90,6 @@
when(mFragment.getFragmentManager()).thenReturn(mFragmentManager);
when(mFragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
- mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
- mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
mVolume = spy(new VolumeInfo("id", 0, null, "id"));
// Note: null is passed as the Lifecycle because we are handling it outside of the normal
// Settings fragment lifecycle for test purposes.
@@ -105,6 +99,51 @@
// Inflate the preference and the widget.
final LayoutInflater inflater = LayoutInflater.from(mContext);
inflater.inflate(mPreference.getLayoutResource(), new LinearLayout(mContext), false);
+
+ mPreferenceScreen = getPreferenceScreen();
+ }
+
+ private PreferenceScreen getPreferenceScreen() {
+ final StorageItemPreference publicStorage = spy(new StorageItemPreference(mContext));
+ publicStorage.setIcon(R.drawable.ic_folder_vd_theme_24);
+ final StorageItemPreference images = spy(new StorageItemPreference(mContext));
+ images.setIcon(R.drawable.ic_photo_library);
+ final StorageItemPreference videos = spy(new StorageItemPreference(mContext));
+ videos.setIcon(R.drawable.ic_local_movies);
+ final StorageItemPreference audios = spy(new StorageItemPreference(mContext));
+ audios.setIcon(R.drawable.ic_media_stream);
+ final StorageItemPreference apps = spy(new StorageItemPreference(mContext));
+ apps.setIcon(R.drawable.ic_storage_apps);
+ final StorageItemPreference games = spy(new StorageItemPreference(mContext));
+ games.setIcon(R.drawable.ic_videogame_vd_theme_24);
+ final StorageItemPreference documentsAndOther = spy(new StorageItemPreference(mContext));
+ documentsAndOther.setIcon(R.drawable.ic_folder_vd_theme_24);
+ final StorageItemPreference system = spy(new StorageItemPreference(mContext));
+ system.setIcon(R.drawable.ic_system_update);
+ final StorageItemPreference trash = spy(new StorageItemPreference(mContext));
+ trash.setIcon(R.drawable.ic_trash_can);
+
+ final PreferenceScreen screen = mock(PreferenceScreen.class);
+ when(screen.findPreference(eq(StorageItemPreferenceController.PUBLIC_STORAGE_KEY)))
+ .thenReturn(publicStorage);
+ when(screen.findPreference(eq(StorageItemPreferenceController.IMAGES_KEY)))
+ .thenReturn(images);
+ when(screen.findPreference(eq(StorageItemPreferenceController.VIDEOS_KEY)))
+ .thenReturn(videos);
+ when(screen.findPreference(eq(StorageItemPreferenceController.AUDIOS_KEY)))
+ .thenReturn(audios);
+ when(screen.findPreference(eq(StorageItemPreferenceController.APPS_KEY)))
+ .thenReturn(apps);
+ when(screen.findPreference(eq(StorageItemPreferenceController.GAMES_KEY)))
+ .thenReturn(games);
+ when(screen.findPreference(eq(StorageItemPreferenceController.DOCUMENTS_AND_OTHER_KEY)))
+ .thenReturn(documentsAndOther);
+ when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
+ .thenReturn(system);
+ when(screen.findPreference(eq(StorageItemPreferenceController.TRASH_KEY)))
+ .thenReturn(trash);
+
+ return screen;
}
@Test
@@ -114,8 +153,26 @@
}
@Test
- public void testClickPhotos() {
- mPreference.setKey("pref_photos_videos");
+ public void launchPublicStorageIntent_nonNullBrowseIntent_settingsIntent() {
+ final String fakeBrowseAction = "FAKE_BROWSE_ACTION";
+ final Intent fakeBrowseIntent = new Intent(fakeBrowseAction);
+ // mContext is not the activity, add FLAG_ACTIVITY_NEW_TASK to avoid AndroidRuntimeException
+ // during this test.
+ fakeBrowseIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ when(mVolume.buildBrowseIntent()).thenReturn(fakeBrowseIntent);
+ mPreference.setKey(StorageItemPreferenceController.PUBLIC_STORAGE_KEY);
+ mController.handlePreferenceTreeClick(mPreference);
+
+ final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(argumentCaptor.capture());
+
+ final Intent intent = argumentCaptor.getValue();
+ assertThat(intent.getAction()).isEqualTo(fakeBrowseAction);
+ }
+
+ @Test
+ public void launchImagesIntent_resolveActionViewNull_settingsIntent() {
+ mPreference.setKey(StorageItemPreferenceController.IMAGES_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -132,8 +189,8 @@
}
@Test
- public void testClickAudio() {
- mPreference.setKey("pref_music_audio");
+ public void launchAudiosIntent_resolveActionViewNull_settingsIntent() {
+ mPreference.setKey(StorageItemPreferenceController.AUDIOS_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -151,16 +208,25 @@
}
@Test
- public void handlePreferenceTreeClick_tappingAudioWhileUninitializedDoesntCrash() {
+ public void setVolume_nullVolume_hidePreferences() {
+ mController.displayPreference(mPreferenceScreen);
+
mController.setVolume(null);
- mPreference.setKey("pref_music_audio");
- mController.handlePreferenceTreeClick(mPreference);
+ assertThat(mController.mPublicStoragePreference.isVisible()).isFalse();
+ assertThat(mController.mImagesPreference.isVisible()).isFalse();
+ assertThat(mController.mVideosPreference.isVisible()).isFalse();
+ assertThat(mController.mAudiosPreference.isVisible()).isFalse();
+ assertThat(mController.mAppsPreference.isVisible()).isFalse();
+ assertThat(mController.mGamesPreference.isVisible()).isFalse();
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isFalse();
+ assertThat(mController.mSystemPreference.isVisible()).isFalse();
+ assertThat(mController.mTrashPreference.isVisible()).isFalse();
}
@Test
- public void testClickApps() {
- mPreference.setKey("pref_other_apps");
+ public void launchAppsIntent_forPersonal_settingsIntent() {
+ mPreference.setKey(StorageItemPreferenceController.APPS_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -177,9 +243,9 @@
}
@Test
- public void testClickAppsForWork() {
+ public void launchAppsIntent_forWork_settingsIntent() {
mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp, true);
- mPreference.setKey("pref_other_apps");
+ mPreference.setKey(StorageItemPreferenceController.APPS_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -204,18 +270,10 @@
}
@Test
- public void handlePreferenceTreeClick_tappingAppsWhileUninitializedDoesntCrash() {
- mController.setVolume(null);
-
- mPreference.setKey("pref_other_apps");
- mController.handlePreferenceTreeClick(mPreference);
- }
-
- @Test
- public void testClickFiles() {
+ public void launchDocumentsAndOtherIntent_resolveActionViewNull_settingsIntent() {
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(mVolume);
when(mVolume.buildBrowseIntent()).thenReturn(new Intent());
- mPreference.setKey("pref_files");
+ mPreference.setKey(StorageItemPreferenceController.DOCUMENTS_AND_OTHER_KEY);
assertThat(mController.handlePreferenceTreeClick(mPreference))
.isTrue();
@@ -227,13 +285,11 @@
Intent browseIntent = mVolume.buildBrowseIntent();
assertThat(intent.getAction()).isEqualTo(browseIntent.getAction());
assertThat(intent.getData()).isEqualTo(browseIntent.getData());
- verify(mMetricsFeatureProvider, times(1))
- .action(nullable(Context.class), eq(MetricsEvent.STORAGE_FILES));
}
@Test
- public void testClickGames() {
- mPreference.setKey("pref_games");
+ public void launchGamesIntent_settingsIntent() {
+ mPreference.setKey(StorageItemPreferenceController.GAMES_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -250,8 +306,8 @@
}
@Test
- public void testClickMovies() {
- mPreference.setKey("pref_movies");
+ public void launchVideosIntent_resolveActionViewNull_settingsIntent() {
+ mPreference.setKey(StorageItemPreferenceController.VIDEOS_KEY);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -269,7 +325,7 @@
@Test
public void testClickSystem() {
- mPreference.setKey("pref_system");
+ mPreference.setKey(StorageItemPreferenceController.SYSTEM_KEY);
assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue();
verify(mFragment.getFragmentManager().beginTransaction())
@@ -279,29 +335,7 @@
@Test
@Config(shadows = ShadowUserManager.class)
public void testMeasurementCompletedUpdatesPreferences() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference movies = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.MOVIES_KEY)))
- .thenReturn(movies);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
mController.setUsedSize(MEGABYTE_IN_BYTES * 970); // There should 870MB attributed.
final StorageAsyncLoader.AppsStorageResult result =
@@ -321,211 +355,113 @@
results.put(0, result);
mController.onLoadFinished(results, 0);
- assertThat(audio.getSummary().toString()).isEqualTo("0.14 GB");
- assertThat(image.getSummary().toString()).isEqualTo("0.35 GB");
- assertThat(games.getSummary().toString()).isEqualTo("0.08 GB");
- assertThat(movies.getSummary().toString()).isEqualTo("0.16 GB");
- assertThat(apps.getSummary().toString()).isEqualTo("0.09 GB");
- assertThat(files.getSummary().toString()).isEqualTo("0.05 GB");
+ assertThat(mController.mImagesPreference.getSummary().toString()).isEqualTo("0.35 GB");
+ assertThat(mController.mVideosPreference.getSummary().toString()).isEqualTo("0.16 GB");
+ assertThat(mController.mAudiosPreference.getSummary().toString()).isEqualTo("0.14 GB");
+ assertThat(mController.mAppsPreference.getSummary().toString()).isEqualTo("0.09 GB");
+ assertThat(mController.mGamesPreference.getSummary().toString()).isEqualTo("0.08 GB");
+ assertThat(mController.mDocumentsAndOtherPreference.getSummary().toString())
+ .isEqualTo("0.05 GB");
}
@Test
public void settingUserIdAppliesNewIcons() {
- final StorageItemPreference audio = spy(new StorageItemPreference(mContext));
- audio.setIcon(R.drawable.ic_media_stream);
- final StorageItemPreference video = spy(new StorageItemPreference(mContext));
- video.setIcon(R.drawable.ic_local_movies);
- final StorageItemPreference image = spy(new StorageItemPreference(mContext));
- image.setIcon(R.drawable.ic_photo_library);
- final StorageItemPreference games = spy(new StorageItemPreference(mContext));
- games.setIcon(R.drawable.ic_videogame_vd_theme_24);
- final StorageItemPreference apps = spy(new StorageItemPreference(mContext));
- apps.setIcon(R.drawable.ic_storage_apps);
- final StorageItemPreference system = spy(new StorageItemPreference(mContext));
- system.setIcon(R.drawable.ic_system_update);
- final StorageItemPreference files = spy(new StorageItemPreference(mContext));
- files.setIcon(R.drawable.ic_folder_vd_theme_24);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.MOVIES_KEY)))
- .thenReturn(video);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
mController.setUserId(new UserHandle(10));
- verify(audio, times(2)).setIcon(nullable(Drawable.class));
- verify(video, times(2)).setIcon(nullable(Drawable.class));
- verify(image, times(2)).setIcon(nullable(Drawable.class));
- verify(games, times(2)).setIcon(nullable(Drawable.class));
- verify(apps, times(2)).setIcon(nullable(Drawable.class));
- verify(system, times(2)).setIcon(nullable(Drawable.class));
- verify(files, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mPublicStoragePreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mImagesPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mVideosPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mAudiosPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mAppsPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mGamesPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mDocumentsAndOtherPreference, times(2))
+ .setIcon(nullable(Drawable.class));
+ verify(mController.mSystemPreference, times(2)).setIcon(nullable(Drawable.class));
+ verify(mController.mTrashPreference, times(2)).setIcon(nullable(Drawable.class));
}
@Test
public void displayPreference_dontHideFilePreferenceWhenEmulatedInternalStorageUsed() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
-
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(mVolume);
+ when(mVolume.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mVolume.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
when(mVolume.isMountedReadable()).thenReturn(true);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
- verify(screen, times(0)).removePreference(files);
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isTrue();
}
@Test
public void displayPreference_hideFilePreferenceWhenEmulatedStorageUnreadable() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
-
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(mVolume);
when(mVolume.isMountedReadable()).thenReturn(false);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
- verify(screen).removePreference(files);
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isFalse();
}
@Test
- public void displayPreference_hideFilePreferenceWhenNoEmulatedInternalStorage() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
-
+ public void displayPreference_noEmulatedInternalStorage_hidePreference() {
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(null);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
- verify(screen).removePreference(files);
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isFalse();
}
@Test
- public void displayPreference_updateFilePreferenceToHideAfterSettingVolume() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
-
+ public void setVolume_updateFilePreferenceToHideAfterSettingVolume_hidePreference() {
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(mVolume);
+ when(mVolume.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mVolume.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
when(mVolume.isMountedReadable()).thenReturn(true);
- mController.displayPreference(screen);
+ mController.displayPreference(mPreferenceScreen);
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(null);
mController.setVolume(mVolume);
- verify(screen).removePreference(files);
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isFalse();
}
@Test
- public void displayPreference_updateFilePreferenceToShowAfterSettingVolume() {
- final StorageItemPreference audio = new StorageItemPreference(mContext);
- final StorageItemPreference image = new StorageItemPreference(mContext);
- final StorageItemPreference games = new StorageItemPreference(mContext);
- final StorageItemPreference apps = new StorageItemPreference(mContext);
- final StorageItemPreference system = new StorageItemPreference(mContext);
- final StorageItemPreference files = new StorageItemPreference(mContext);
- final PreferenceScreen screen = mock(PreferenceScreen.class);
- when(screen.findPreference(eq(StorageItemPreferenceController.GAME_KEY)))
- .thenReturn(games);
- when(screen.findPreference(eq(StorageItemPreferenceController.AUDIO_KEY)))
- .thenReturn(audio);
- when(screen.findPreference(eq(StorageItemPreferenceController.PHOTO_KEY)))
- .thenReturn(image);
- when(screen.findPreference(eq(StorageItemPreferenceController.FILES_KEY)))
- .thenReturn(files);
- when(screen.findPreference(eq(StorageItemPreferenceController.SYSTEM_KEY)))
- .thenReturn(system);
- when(screen.findPreference(eq(StorageItemPreferenceController.OTHER_APPS_KEY)))
- .thenReturn(apps);
-
+ public void setVolume_updateFilePreferenceToShowAfterSettingVolume_showPreference() {
// This will hide it initially.
- mController.displayPreference(screen);
-
+ mController.displayPreference(mPreferenceScreen);
when(mSvp.findEmulatedForPrivate(nullable(VolumeInfo.class))).thenReturn(mVolume);
+ when(mVolume.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+ when(mVolume.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
when(mVolume.isMountedReadable()).thenReturn(true);
// And we bring it back.
mController.setVolume(mVolume);
- verify(screen).addPreference(files);
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isTrue();
+ }
+
+ @Test
+ public void setVolume_publicStorage_showFilePreference() {
+ // This will hide it initially.
+ mController.displayPreference(mPreferenceScreen);
+ when(mVolume.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
+ when(mVolume.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+ when(mVolume.isMountedReadable()).thenReturn(true);
+
+ // And we bring it back.
+ mController.setVolume(mVolume);
+
+ assertThat(mController.mPublicStoragePreference.isVisible()).isTrue();
+ assertThat(mController.mImagesPreference.isVisible()).isFalse();
+ assertThat(mController.mVideosPreference.isVisible()).isFalse();
+ assertThat(mController.mAudiosPreference.isVisible()).isFalse();
+ assertThat(mController.mAppsPreference.isVisible()).isFalse();
+ assertThat(mController.mGamesPreference.isVisible()).isFalse();
+ assertThat(mController.mDocumentsAndOtherPreference.isVisible()).isFalse();
+ assertThat(mController.mSystemPreference.isVisible()).isFalse();
+ assertThat(mController.mTrashPreference.isVisible()).isFalse();
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.java
new file mode 100644
index 0000000..fdf005d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public abstract class BasePrivacySettingsPreferenceTest {
+
+ protected Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ }
+
+ protected static void verifyEnterpriseSearchIndexableResources(
+ List<SearchIndexableResource> searchIndexableResources) {
+ assertThat(searchIndexableResources).isNotEmpty();
+ assertThat(searchIndexableResources.size()).isEqualTo(1);
+ assertThat(searchIndexableResources.get(0).xmlResId)
+ .isEqualTo(R.xml.enterprise_privacy_settings);
+ }
+
+ protected static void verifyEnterprisePreferenceControllers(
+ List<AbstractPreferenceController> controllers) {
+ assertThat(controllers).isNotNull();
+ assertThat(controllers.size()).isEqualTo(17);
+ int position = 0;
+ assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ SecurityLogsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ EnterpriseInstalledPackagesPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ AdminGrantedLocationPermissionsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ AdminGrantedMicrophonePermissionPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ AdminGrantedCameraPermissionPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ EnterpriseSetDefaultAppsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ AlwaysOnVpnCurrentUserPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ AlwaysOnVpnManagedProfilePreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ GlobalHttpProxyPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ CaCertsCurrentUserPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ CaCertsManagedProfilePreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ PreferenceCategoryController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ FailedPasswordWipeCurrentUserPreferenceController.class);
+ assertThat(controllers.get(position)).isInstanceOf(
+ FailedPasswordWipeManagedProfilePreferenceController.class);
+ }
+
+ protected static void verifyFinancedSearchIndexableResources(
+ List<SearchIndexableResource> searchIndexableResources) {
+ assertThat(searchIndexableResources).isNotEmpty();
+ assertThat(searchIndexableResources.size()).isEqualTo(1);
+ assertThat(searchIndexableResources.get(0).xmlResId)
+ .isEqualTo(R.xml.financed_privacy_settings);
+ }
+
+ protected static void verifyFinancedPreferenceControllers(
+ List<AbstractPreferenceController> controllers) {
+ assertThat(controllers).isNotNull();
+ assertThat(controllers.size()).isEqualTo(6);
+ int position = 0;
+ assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ SecurityLogsPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ EnterpriseInstalledPackagesPreferenceController.class);
+ assertThat(controllers.get(position++)).isInstanceOf(
+ PreferenceCategoryController.class);
+ assertThat(controllers.get(position)).isInstanceOf(
+ FailedPasswordWipeCurrentUserPreferenceController.class);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
index 6858dd3..8052357 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacyPreferenceControllerTest.java
@@ -16,10 +16,15 @@
package com.android.settings.enterprise;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import androidx.preference.Preference;
@@ -42,9 +47,14 @@
private static final String MANAGED_WITH_NAME = "managed by Foo, Inc.";
private static final String MANAGING_ORGANIZATION = "Foo, Inc.";
private static final String KEY_ENTERPRISE_PRIVACY = "enterprise_privacy";
+ private static final String FINANCED_PREFERENCE_TITLE = "Financed device info";
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("com.android.foo", "bar");
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
private FakeFeatureFactory mFeatureFactory;
private EnterprisePrivacyPreferenceController mController;
@@ -54,6 +64,14 @@
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mController = new EnterprisePrivacyPreferenceController(mContext);
+
+ when((Object) mContext.getSystemService(DevicePolicyManager.class))
+ .thenReturn(mDevicePolicyManager);
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
}
@Test
@@ -77,6 +95,25 @@
}
@Test
+ public void testUpdateState_verifyPreferenceTitleIsUpdatedForFinancedDevice() {
+ final Preference preference = new Preference(mContext, null, 0, 0);
+ when(mContext.getResources().getString(
+ R.string.enterprise_privacy_settings_summary_with_name, MANAGING_ORGANIZATION))
+ .thenReturn(MANAGED_WITH_NAME);
+ when(mContext.getString(R.string.financed_privacy_settings))
+ .thenReturn(FINANCED_PREFERENCE_TITLE);
+ when(mFeatureFactory.enterprisePrivacyFeatureProvider.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ mController.updateState(preference);
+
+ assertThat(preference.getTitle()).isEqualTo(FINANCED_PREFERENCE_TITLE);
+ assertThat(preference.getSummary()).isEqualTo(MANAGED_WITH_NAME);
+ }
+
+ @Test
public void testIsAvailable() {
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index 2d4ba62..eb70749 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -16,47 +16,70 @@
package com.android.settings.enterprise;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
-import android.content.Context;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.provider.SearchIndexableResource;
+
+import androidx.test.core.app.ApplicationProvider;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.drawer.CategoryKey;
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;
-import org.robolectric.RuntimeEnvironment;
+import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-public class EnterprisePrivacySettingsTest {
+public class EnterprisePrivacySettingsTest extends BasePrivacySettingsPreferenceTest {
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("com.android.foo", "bar");
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private Context mContext;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private PrivacySettingsPreference mPrivacySettingsPreference;
private FakeFeatureFactory mFeatureFactory;
private EnterprisePrivacySettings mSettings;
+ @Override
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSettings = new EnterprisePrivacySettings();
+ mSettings.mPrivacySettingsPreference = mPrivacySettingsPreference;
+
+ when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
}
@Test
public void verifyConstants() {
+ when(mPrivacySettingsPreference.getPreferenceScreenResId())
+ .thenReturn(R.xml.enterprise_privacy_settings);
+
assertThat(mSettings.getMetricsCategory())
.isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS);
assertThat(mSettings.getLogTag()).isEqualTo("EnterprisePrivacySettings");
@@ -76,6 +99,7 @@
@Test
public void isPageEnabled_noDeviceOwner_shouldReturnFalse() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner())
.thenReturn(false);
@@ -85,53 +109,35 @@
@Test
public void getPreferenceControllers() {
- final List<AbstractPreferenceController> controllers =
- mSettings.createPreferenceControllers(RuntimeEnvironment.application);
- verifyPreferenceControllers(controllers);
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new NetworkLogsPreferenceController(mContext));
+ when(mPrivacySettingsPreference.createPreferenceControllers(anyBoolean()))
+ .thenReturn(controllers);
+
+ final List<AbstractPreferenceController> privacyControllers =
+ mSettings.createPreferenceControllers(mContext);
+
+ assertThat(privacyControllers).isNotNull();
+ assertThat(privacyControllers.size()).isEqualTo(1);
+ assertThat(controllers.get(0)).isInstanceOf(NetworkLogsPreferenceController.class);
}
@Test
- public void getSearchIndexProviderPreferenceControllers() {
+ public void
+ getSearchIndexProviderPreferenceControllers_returnsEnterpriseSearchIndexPreferenceControllers() {
final List<AbstractPreferenceController> controllers =
EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
- .getPreferenceControllers(RuntimeEnvironment.application);
- verifyPreferenceControllers(controllers);
+ .getPreferenceControllers(mContext);
+
+ verifyEnterprisePreferenceControllers(controllers);
}
- private void verifyPreferenceControllers(List<AbstractPreferenceController> controllers) {
- assertThat(controllers).isNotNull();
- assertThat(controllers.size()).isEqualTo(17);
- int position = 0;
- assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- SecurityLogsPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- EnterpriseInstalledPackagesPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- AdminGrantedLocationPermissionsPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- AdminGrantedMicrophonePermissionPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- AdminGrantedCameraPermissionPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- EnterpriseSetDefaultAppsPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- AlwaysOnVpnCurrentUserPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- AlwaysOnVpnManagedProfilePreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- GlobalHttpProxyPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- CaCertsCurrentUserPreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- CaCertsManagedProfilePreferenceController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- PreferenceCategoryController.class);
- assertThat(controllers.get(position++)).isInstanceOf(
- FailedPasswordWipeCurrentUserPreferenceController.class);
- assertThat(controllers.get(position)).isInstanceOf(
- FailedPasswordWipeManagedProfilePreferenceController.class);
+ @Test
+ public void getXmlResourcesToIndex_returnsEnterpriseXmlResources() {
+ final List<SearchIndexableResource> searchIndexableResources =
+ EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
+ .getXmlResourcesToIndex(mContext, true);
+
+ verifyEnterpriseSearchIndexableResources(searchIndexableResources);
}
}
diff --git a/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java
new file mode 100644
index 0000000..68e37fc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class PrivacySettingsEnterprisePreferenceTest extends BasePrivacySettingsPreferenceTest {
+
+ private PrivacySettingsEnterprisePreference mPrivacySettingsEnterprisePreference;
+
+ @Override
+ @Before
+ public void setUp() {
+ super.setUp();
+ mPrivacySettingsEnterprisePreference = new PrivacySettingsEnterprisePreference(mContext);
+ }
+
+ @Test
+ public void getPreferenceScreenResId() {
+ assertThat(mPrivacySettingsEnterprisePreference.getPreferenceScreenResId())
+ .isEqualTo(R.xml.enterprise_privacy_settings);
+ }
+
+ @Test
+ public void getXmlResourcesToIndex() {
+ final List<SearchIndexableResource> searchIndexableResources =
+ mPrivacySettingsEnterprisePreference.getXmlResourcesToIndex();
+
+ verifyEnterpriseSearchIndexableResources(searchIndexableResources);
+ }
+
+ @Test
+ public void getPreferenceControllers() {
+ final List<AbstractPreferenceController> controllers =
+ mPrivacySettingsEnterprisePreference.createPreferenceControllers(true);
+
+ verifyEnterprisePreferenceControllers(controllers);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java
new file mode 100644
index 0000000..fe7b214
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class PrivacySettingsFinancedPreferenceTest extends BasePrivacySettingsPreferenceTest {
+
+ private PrivacySettingsFinancedPreference mPrivacySettingsFinancedPreference;
+
+ @Override
+ @Before
+ public void setUp() {
+ super.setUp();
+ mPrivacySettingsFinancedPreference = new PrivacySettingsFinancedPreference(mContext);
+ }
+
+ @Test
+ public void getPreferenceScreenResId() {
+ assertThat(mPrivacySettingsFinancedPreference.getPreferenceScreenResId())
+ .isEqualTo(R.xml.financed_privacy_settings);
+ }
+
+ @Test
+ public void getXmlResourcesToIndex() {
+ final List<SearchIndexableResource> searchIndexableResources =
+ mPrivacySettingsFinancedPreference.getXmlResourcesToIndex();
+
+ verifyFinancedSearchIndexableResources(searchIndexableResources);
+ }
+
+ @Test
+ public void getPreferenceControllers() {
+ final List<AbstractPreferenceController> controllers =
+ mPrivacySettingsFinancedPreference.createPreferenceControllers(true);
+
+ verifyFinancedPreferenceControllers(controllers);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
index b3b8196..52f682c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatterySaverControllerTest.java
@@ -105,7 +105,8 @@
Settings.Global.AUTOMATIC_POWER_SAVE_MODE,
PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC);
- assertThat(mBatterySaverController.getSummary()).isEqualTo("Based on your routine");
+ assertThat(mBatterySaverController.getSummary()).
+ isEqualTo("Will turn on based on your routine");
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 775ca40..744db8c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -277,7 +277,7 @@
@Test
public void testCalculateLastFullChargeTime() {
final long currentTimeMs = System.currentTimeMillis();
- when(mBatteryUsageStats.getStatsStartRealtime()).thenReturn(
+ when(mBatteryUsageStats.getStatsStartTimestamp()).thenReturn(
currentTimeMs - TIME_SINCE_LAST_FULL_CHARGE_MS);
assertThat(mBatteryUtils.calculateLastFullChargeTime(mBatteryUsageStats, currentTimeMs))
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
index a1f3bda..b132a97 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/SmartBatteryPreferenceControllerTest.java
@@ -21,9 +21,10 @@
import static org.mockito.Mockito.doReturn;
import android.content.ContentResolver;
-import android.content.Context;
import android.provider.Settings;
+import androidx.preference.SwitchPreference;
+
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.widget.MainSwitchPreference;
@@ -42,50 +43,48 @@
private static final int OFF = 0;
private SmartBatteryPreferenceController mController;
+ private SwitchPreference mPreference;
private ContentResolver mContentResolver;
private FakeFeatureFactory mFeatureFactory;
- private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
mFeatureFactory = FakeFeatureFactory.setupForTest();
mContentResolver = RuntimeEnvironment.application.getContentResolver();
mController = new SmartBatteryPreferenceController(RuntimeEnvironment.application);
+ mPreference = new SwitchPreference(RuntimeEnvironment.application);
}
@Test
public void testUpdateState_smartBatteryOn_preferenceChecked() {
putSmartBatteryValue(ON);
- final MainSwitchPreference preference = new MainSwitchPreference(mContext);
- mController.updateState(preference);
+ mController.updateState(mPreference);
- assertThat(preference.isChecked()).isTrue();
+ assertThat(mPreference.isChecked()).isTrue();
}
@Test
public void testUpdateState_smartBatteryOff_preferenceUnchecked() {
putSmartBatteryValue(OFF);
- final MainSwitchPreference preference = new MainSwitchPreference(mContext);
- mController.updateState(preference);
+ mController.updateState(mPreference);
- assertThat(preference.isChecked()).isFalse();
+ assertThat(mPreference.isChecked()).isFalse();
}
@Test
public void testUpdateState_checkPreference_smartBatteryOn() {
- mController.onSwitchChanged(null, true);
+ mController.onPreferenceChange(mPreference, true);
assertThat(getSmartBatteryValue()).isEqualTo(ON);
}
@Test
public void testUpdateState_unCheckPreference_smartBatteryOff() {
- mController.onSwitchChanged(null, false);
+ mController.onPreferenceChange(mPreference, false);
assertThat(getSmartBatteryValue()).isEqualTo(OFF);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceControllerTest.java
index 435aa88..b01e3b1 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryManagerPreferenceControllerTest.java
@@ -63,31 +63,20 @@
}
@Test
- public void updateState_smartBatteryOnWithRestrictApps_showSummary() {
- mController.updateSummary(mPreference, true /* smartBatteryOn */, 2);
+ public void updateState_smartBatteryWithRestrictApps_showSummary() {
+ mController.updateSummary(mPreference, 2);
assertThat(mPreference.getSummary()).isEqualTo("2 apps restricted");
}
@Test
- public void updateState_smartBatteryOnWithoutRestriction_showSummary() {
+ public void updateState_smartBatteryWithoutRestriction_showSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isSmartBatterySupported()).thenReturn(true);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, ON);
mController.updateState(mPreference);
- assertThat(mPreference.getSummary()).isEqualTo("On / Detecting when apps drain battery");
- }
-
- @Test
- public void updateState_smartBatteryOff_showSummary() {
- when(mFeatureFactory.powerUsageFeatureProvider.isSmartBatterySupported()).thenReturn(true);
- Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, OFF);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.getSummary()).isEqualTo("Off");
+ assertThat(mPreference.getSummary()).isEqualTo("Detecting when apps drain battery");
}
}
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 82448d1..5d7b082 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -54,7 +54,6 @@
BatteryTip.TipType.BATTERY_SAVER,
BatteryTip.TipType.HIGH_DEVICE_USAGE,
BatteryTip.TipType.LOW_BATTERY,
- BatteryTip.TipType.SUMMARY,
BatteryTip.TipType.SMART_BATTERY_MANAGER};
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private BatteryUsageStats mBatteryUsageStats;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
index b68a8f5..e919288 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
@@ -20,7 +20,6 @@
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;
@@ -30,10 +29,6 @@
import android.os.Bundle;
import android.text.format.DateUtils;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto;
@@ -49,7 +44,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;
@@ -104,10 +98,24 @@
}
@Test
- public void testDisplayPreference_addSummaryTip() {
+ public void testDisplayPreference_isInvisible() {
mBatteryTipPreferenceController.displayPreference(mPreferenceScreen);
- assertOnlyContainsSummaryTip(mCardPreference);
+ assertThat(mCardPreference.isVisible()).isFalse();
+ }
+
+ @Test
+ public void testUpdateBatteryTips_tipsStateNew_isVisible() {
+ mBatteryTipPreferenceController.updateBatteryTips(mOldBatteryTips);
+
+ assertThat(mCardPreference.isVisible()).isTrue();
+ }
+
+ @Test
+ public void testUpdateBatteryTips_tipsStateInvisible_isInvisible() {
+ mBatteryTipPreferenceController.updateBatteryTips(mNewBatteryTips);
+
+ assertThat(mCardPreference.isVisible()).isFalse();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
index 6199788..6954f9a 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipUtilsTest.java
@@ -24,7 +24,6 @@
import com.android.settings.SettingsActivity;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.fuelgauge.batterytip.actions.BatteryDefenderAction;
-import com.android.settings.fuelgauge.batterytip.actions.BatterySaverAction;
import com.android.settings.fuelgauge.batterytip.actions.OpenBatterySaverAction;
import com.android.settings.fuelgauge.batterytip.actions.OpenRestrictAppFragmentAction;
import com.android.settings.fuelgauge.batterytip.actions.RestrictAppAction;
@@ -90,34 +89,18 @@
}
@Test
- public void testGetActionForBatteryTip_typeEarlyWarningStateNew_returnActionBatterySaver() {
+ public void testGetActionForBatteryTip_typeEarlyWarningStateNew_returnActionOpen() {
when(mEarlyWarningTip.getState()).thenReturn(BatteryTip.StateType.NEW);
assertThat(BatteryTipUtils.getActionForBatteryTip(mEarlyWarningTip, mSettingsActivity,
- mFragment)).isInstanceOf(BatterySaverAction.class);
- }
-
- @Test
- public void testGetActionForBatteryTip_typeEarlyWarningStateHandled_returnActionOpen() {
- when(mEarlyWarningTip.getState()).thenReturn(BatteryTip.StateType.HANDLED);
-
- assertThat(BatteryTipUtils.getActionForBatteryTip(mEarlyWarningTip, mSettingsActivity,
mFragment)).isInstanceOf(OpenBatterySaverAction.class);
}
@Test
- public void testGetActionForBatteryTip_typeLowBatteryStateNew_returnActionBatterySaver() {
+ public void testGetActionForBatteryTip_typeLowBatteryStateNew_returnActionOpen() {
when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.NEW);
assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
- mFragment)).isInstanceOf(BatterySaverAction.class);
- }
-
- @Test
- public void testGetActionForBatteryTip_typeLowBatteryStateHandled_returnActionOpen() {
- when(mLowBatteryTip.getState()).thenReturn(BatteryTip.StateType.HANDLED);
-
- assertThat(BatteryTipUtils.getActionForBatteryTip(mLowBatteryTip, mSettingsActivity,
mFragment)).isInstanceOf(OpenBatterySaverAction.class);
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java
index dbee7f1..84411a7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/EarlyWarningDetectorTest.java
@@ -86,11 +86,11 @@
}
@Test
- public void testDetect_batterySaverOn_tipHandled() {
+ public void testDetect_batterySaverOn_tipInvisible() {
doReturn(true).when(mPowerManager).isPowerSaveMode();
assertThat(mEarlyWarningDetector.detect().getState())
- .isEqualTo(BatteryTip.StateType.HANDLED);
+ .isEqualTo(BatteryTip.StateType.INVISIBLE);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
index 55020e2..245bacc 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/LowBatteryDetectorTest.java
@@ -91,10 +91,11 @@
}
@Test
- public void testDetect_batterySaverOn_tipHandled() {
+ public void testDetect_batterySaverOn_tipInvisible() {
mShadowPowerManager.setIsPowerSaveMode(true);
- assertThat(mLowBatteryDetector.detect().getState()).isEqualTo(BatteryTip.StateType.HANDLED);
+ assertThat(mLowBatteryDetector.detect().getState())
+ .isEqualTo(BatteryTip.StateType.INVISIBLE);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetectorTest.java
index 477bf49..00dcbd4 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SmartBatteryDetectorTest.java
@@ -22,26 +22,37 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.os.PowerManager;
import android.provider.Settings;
+import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.BatteryTipPolicy;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
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.Shadows;
+import org.robolectric.shadows.ShadowPowerManager;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class SmartBatteryDetectorTest {
+ private static final int EXPECTED_BATTERY_LEVEL = 30;
+ private static final int UNEXPECTED_BATTERY_LEVEL = 31;
+
private Context mContext;
private ContentResolver mContentResolver;
private SmartBatteryDetector mSmartBatteryDetector;
private BatteryTipPolicy mPolicy;
+ private ShadowPowerManager mShadowPowerManager;
+ @Mock
+ private BatteryInfo mBatteryInfo;
@Before
public void setUp() {
@@ -50,7 +61,9 @@
mContext = RuntimeEnvironment.application;
mContentResolver = mContext.getContentResolver();
mPolicy = spy(new BatteryTipPolicy(mContext));
- mSmartBatteryDetector = new SmartBatteryDetector(mPolicy, mContentResolver);
+ mShadowPowerManager = Shadows.shadowOf(mContext.getSystemService(PowerManager.class));
+ mSmartBatteryDetector =
+ new SmartBatteryDetector(mContext, mPolicy, mBatteryInfo, mContentResolver);
}
@Test
@@ -64,14 +77,38 @@
public void testDetect_smartBatteryOff_tipVisible() {
Settings.Global.putInt(mContentResolver,
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 0);
+ mShadowPowerManager.setIsPowerSaveMode(false);
+ mBatteryInfo.batteryLevel = EXPECTED_BATTERY_LEVEL;
assertThat(mSmartBatteryDetector.detect().isVisible()).isTrue();
}
@Test
+ public void testDetect_batterySaverOn_tipInvisible() {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 0);
+ mShadowPowerManager.setIsPowerSaveMode(true);
+ mBatteryInfo.batteryLevel = EXPECTED_BATTERY_LEVEL;
+
+ assertThat(mSmartBatteryDetector.detect().isVisible()).isFalse();
+ }
+
+ @Test
+ public void testDetect_unexpectedBatteryLevel_tipInvisible() {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 0);
+ mShadowPowerManager.setIsPowerSaveMode(true);
+ mBatteryInfo.batteryLevel = UNEXPECTED_BATTERY_LEVEL;
+
+ assertThat(mSmartBatteryDetector.detect().isVisible()).isFalse();
+ }
+
+ @Test
public void testDetect_smartBatteryOn_tipInvisible() {
Settings.Global.putInt(mContentResolver,
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1);
+ mShadowPowerManager.setIsPowerSaveMode(false);
+ mBatteryInfo.batteryLevel = EXPECTED_BATTERY_LEVEL;
assertThat(mSmartBatteryDetector.detect().isVisible()).isFalse();
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java
index 0fd3ccf..5794b6c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/SummaryDetectorTest.java
@@ -54,9 +54,9 @@
}
@Test
- public void testDetect_notDisabled_tipVisible() {
+ public void testDetect_notDisabled_tipInvisible() {
SummaryDetector detector = new SummaryDetector(mPolicy, AVERAGE_TIME_MS);
- assertThat(detector.detect().isVisible()).isTrue();
+ assertThat(detector.detect().isVisible()).isFalse();
}
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
index 85b00c8..aeea10f 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/EarlyWarningTipTest.java
@@ -74,24 +74,13 @@
}
@Test
- public void testInfo_stateHandled_displayPowerModeHandledInfo() {
- final EarlyWarningTip tip =
- new EarlyWarningTip(BatteryTip.StateType.HANDLED, false /* powerModeOn */);
-
- assertThat(tip.getTitle(mContext)).isEqualTo("Battery Saver is on");
- assertThat(tip.getSummary(mContext)).isEqualTo("Some features may be limited");
- assertThat(tip.getIconId()).isEqualTo(R.drawable.ic_battery_status_maybe_24dp);
- assertThat(tip.getIconTintColorId()).isEqualTo(R.color.battery_maybe_color_light);
- }
-
- @Test
- public void testUpdate_powerModeTurnedOn_typeBecomeHandled() {
+ public void testUpdate_powerModeTurnedOn_typeBecomeInvisible() {
final EarlyWarningTip nextTip =
new EarlyWarningTip(BatteryTip.StateType.INVISIBLE, true /* powerModeOn */);
mEarlyWarningTip.updateState(nextTip);
- assertThat(mEarlyWarningTip.getState()).isEqualTo(BatteryTip.StateType.HANDLED);
+ assertThat(mEarlyWarningTip.getState()).isEqualTo(BatteryTip.StateType.INVISIBLE);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
index 11dffcd..244faea 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/tips/LowBatteryTipTest.java
@@ -65,13 +65,6 @@
}
@Test
- public void getSummary_tipHandled_showSummary() {
- mLowBatteryTip.mState = BatteryTip.StateType.HANDLED;
-
- assertThat(mLowBatteryTip.getSummary(mContext)).isEqualTo("Some features may be limited");
- }
-
- @Test
public void getSummary_tipNew_showSummary() {
mLowBatteryTip.mState = BatteryTip.StateType.NEW;
diff --git a/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java
new file mode 100644
index 0000000..de58c99
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/gestures/LongPressPowerButtonPreferenceControllerTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.gestures;
+
+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.app.Application;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class LongPressPowerButtonPreferenceControllerTest {
+
+ private static final String KEY_LONG_PRESS_POWER_BUTTON =
+ "gesture_power_menu_long_press_for_assist";
+
+ private Application mContext;
+ private Resources mResources;
+ private LongPressPowerButtonPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mResources = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+ mController = new LongPressPowerButtonPreferenceController(mContext,
+ KEY_LONG_PRESS_POWER_BUTTON);
+ }
+
+ @Test
+ public void isAvailable_configIsTrue_shouldReturnTrue() {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_configIsFalse_shouldReturnFalse() {
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
+
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void preferenceChecked_longPressPowerSettingSetToAssistant() {
+ mController.onPreferenceChange(null, true);
+
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(
+ LongPressPowerButtonPreferenceController.LONG_PRESS_POWER_ASSISTANT_VALUE);
+ }
+
+ @Test
+ public void preferenceUnchecked_longPressPowerSettingSetToDefaultValue() {
+ // Value out of range chosen deliberately.
+ when(mResources.getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerBehavior))
+ .thenReturn(8);
+
+ mController.onPreferenceChange(null, false);
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(8);
+ }
+
+ @Test
+ public void preferenceUnchecked_assistDefault_setShutOff() {
+ // Value out of range chosen deliberately.
+ when(mResources.getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerBehavior))
+ .thenReturn(
+ LongPressPowerButtonPreferenceController.LONG_PRESS_POWER_ASSISTANT_VALUE);
+
+ mController.onPreferenceChange(null, false);
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(
+ LongPressPowerButtonPreferenceController.LONG_PRESS_POWER_SHUT_OFF);
+ }
+
+ @Test
+ public void preferenceUnchecked_assistDefaultGlobalActionsEnabled_setGlobalActions() {
+ // Value out of range chosen deliberately.
+ when(mResources.getInteger(
+ com.android.internal.R.integer.config_longPressOnPowerBehavior))
+ .thenReturn(
+ LongPressPowerButtonPreferenceController.LONG_PRESS_POWER_ASSISTANT_VALUE);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ LongPressPowerButtonPreferenceController.CARDS_AVAILABLE_KEY, 1);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ LongPressPowerButtonPreferenceController.CARDS_ENABLED_KEY, 1);
+
+ mController.onPreferenceChange(null, false);
+ assertThat(Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_LONG_PRESS, -1)).isEqualTo(
+ LongPressPowerButtonPreferenceController.LONG_PRESS_POWER_GLOBAL_ACTIONS);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
index 11128f3..b53629e 100644
--- a/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/OneHandedEnablePreferenceControllerTest.java
@@ -51,14 +51,16 @@
public void setChecked_setBoolean_checkIsTrueOrFalse() {
mController.setChecked(false);
assertThat(OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)).isFalse();
+ assertThat(OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext)).isTrue();
mController.setChecked(true);
assertThat(OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)).isTrue();
+ assertThat(OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext)).isFalse();
}
@Test
public void getAvailabilityStatus_setSupportOneHandedModeProperty_shouldAvailable() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.AVAILABLE);
@@ -66,7 +68,7 @@
@Test
public void getAvailabilityStatus_unsetSupportOneHandedModeProperty_shouldUnsupported() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "false");
assertThat(mController.getAvailabilityStatus())
.isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
index fcea919..2651708 100644
--- a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
@@ -55,7 +55,7 @@
@Test
public void isPageSearchEnabled_setSupportOneHandedModeProperty_shouldReturnTrue() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
final Object obj = ReflectionHelpers.callInstanceMethod(
OneHandedSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
@@ -66,7 +66,7 @@
@Test
public void isPageSearchEnabled_unsetSupportOneHandedModeProperty_shouldReturnFalse() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "false");
final Object obj = ReflectionHelpers.callInstanceMethod(
OneHandedSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
diff --git a/tests/robotests/src/com/android/settings/gestures/PowerMenuPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/PowerMenuPreferenceControllerTest.java
index 570a680..f8d81f2 100644
--- a/tests/robotests/src/com/android/settings/gestures/PowerMenuPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/PowerMenuPreferenceControllerTest.java
@@ -18,8 +18,13 @@
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.content.pm.PackageManager;
+import android.content.res.Resources;
import android.provider.Settings;
import com.android.settings.core.BasePreferenceController;
@@ -35,6 +40,7 @@
@RunWith(RobolectricTestRunner.class)
public class PowerMenuPreferenceControllerTest {
private Context mContext;
+ private Resources mResources;
private PowerMenuPreferenceController mController;
private ShadowPackageManager mShadowPackageManager;
@@ -44,17 +50,26 @@
private static final String CARDS_ENABLED = Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED;
private static final String CARDS_AVAILABLE = Settings.Secure.GLOBAL_ACTIONS_PANEL_AVAILABLE;
+
@Before
public void setUp() {
- mContext = RuntimeEnvironment.application;
+ mContext = spy(RuntimeEnvironment.application);
+ mResources = mock(Resources.class);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+ when(mContext.getResources()).thenReturn(mResources);
mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
mController = new PowerMenuPreferenceController(mContext, KEY_GESTURE_POWER_MENU);
}
@Test
- public void getAvailabilityStatus_bothAvailable_available() {
+ public void getAvailabilityStatus_allAvailable_available() {
Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 1);
mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, true);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
@@ -64,6 +79,9 @@
public void getAvailabilityStatus_onlyCardsAvailable_available() {
Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 1);
mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, false);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
@@ -73,15 +91,69 @@
public void getAvailabilityStatus_onlyControlsAvailable_available() {
Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 0);
mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, true);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.AVAILABLE);
}
@Test
- public void getAvailabilityStatus_bothUnavailable_unavailable() {
+ public void getAvailabilityStatus_controlsAndCardsAvailable_available() {
+ Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 1);
+ mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, true);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_controlsAndAssistAvailable_available() {
+ Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 0);
+ mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, true);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_cardsAndAssistAvailable_available() {
+ Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 1);
+ mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, false);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_onlyAssistAvailable_available() {
Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 0);
mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, false);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_allUnavailable_unavailable() {
+ Settings.Secure.putInt(mContext.getContentResolver(), CARDS_AVAILABLE, 0);
+ mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, false);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
+ .thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
index a1c0c8f..9f76800 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
@@ -16,10 +16,7 @@
package com.android.settings.gestures;
-import static android.provider.Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
-
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;
@@ -28,6 +25,7 @@
import android.provider.Settings;
import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
import org.junit.Before;
import org.junit.Test;
@@ -57,44 +55,33 @@
public void setChecked_toggledOn_enablesSwipeBottomToNotification() {
mController.setChecked(true);
- assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
- SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1)).isEqualTo(1);
+ assertThat(OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext)).isTrue();
+ assertThat(OneHandedSettingsUtils.isOneHandedModeEnabled(mContext)).isFalse();
}
@Test
public void setChecked_toggledOff_disablesSwipeBottomToNotification() {
mController.setChecked(false);
- assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
- SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1)).isEqualTo(0);
+ assertThat(OneHandedSettingsUtils.isSwipeDownNotificationEnabled(mContext)).isFalse();
}
@Test
- public void getAvailabilityStatus_oneHandedUnsupported_returnsAvailable() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+ public void getAvailabilityStatus_oneHandedUnsupported_returnsUnsupport() {
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "false");
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_oneHandedSupported_returnsAvailable() {
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
- public void getAvailabilityStatus_oneHandedDisabled_returnsAvailable() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
-
- assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
- }
-
- @Test
- public void getAvailabilityStatus_oneHandedEnabled_returnsDisabled() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
-
- assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
- }
-
- @Test
public void getSummary_gestureEnabled_returnsOnSummary() {
mController.setChecked(true);
@@ -109,4 +96,13 @@
assertThat(mController.getSummary()).isEqualTo(
mContext.getText(R.string.gesture_setting_off));
}
+
+ @Test
+ public void isChecked_getDefaultConfig_returnFalse() {
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "false");
+ Settings.Secure.resetToDefaults(mContext.getContentResolver(),
+ Settings.Secure.ONE_HANDED_MODE_ENABLED);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java
index ad8104c..3a65b26 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationSettingsTest.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.os.SystemProperties;
import android.provider.SearchIndexableResource;
-import android.provider.Settings;
import com.android.settings.R;
@@ -64,22 +63,21 @@
}
@Test
- public void isPageSearchEnabled_oneHandedUnsupported_shouldReturnTrue() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+ public void isPageSearchEnabled_oneHandedUnsupported_shouldReturnFalse() {
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "false");
final Object obj = ReflectionHelpers.callInstanceMethod(
SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
ReflectionHelpers.ClassParameter.from(Context.class, mContext));
final boolean isEnabled = (Boolean) obj;
- assertThat(isEnabled).isTrue();
+ assertThat(isEnabled).isFalse();
}
@Test
public void isPageSearchEnabled_oneHandedDisabled_shouldReturnTrue() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 0);
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
+ OneHandedSettingsUtils.setSettingsOneHandedModeEnabled(mContext, false);
final Object obj = ReflectionHelpers.callInstanceMethod(
SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
@@ -91,9 +89,8 @@
@Test
public void isPageSearchEnabled_oneHandedEnabled_shouldReturnFalse() {
- SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "true");
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
+ SystemProperties.set(OneHandedSettingsUtils.SUPPORT_ONE_HANDED_MODE, "true");
+ OneHandedSettingsUtils.setSettingsOneHandedModeEnabled(mContext, true);
final Object obj = ReflectionHelpers.callInstanceMethod(
SwipeBottomToNotificationSettings.SEARCH_INDEX_DATA_PROVIDER, "isPageSearchEnabled",
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index 173f625..c7a2650 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -25,16 +25,20 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.os.Build;
-import android.util.FeatureFlagUtils;
+import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import androidx.fragment.app.Fragment;
+
import com.android.settings.R;
-import com.android.settings.core.FeatureFlags;
import com.android.settings.core.HideNonSystemOverlayMixin;
+import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
+import com.android.settings.testutils.shadow.ShadowUserManager;
import org.junit.Before;
import org.junit.Test;
@@ -46,15 +50,20 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowActivityManager;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowUserManager.class,
+ SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class})
public class SettingsHomepageActivityTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- FeatureFlagUtils.setEnabled(RuntimeEnvironment.application, FeatureFlags.SILKY_HOME, false);
}
@Test
@@ -67,6 +76,77 @@
}
@Test
+ public void launch_configDisabled_shouldHideAvatar() {
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+
+ final View avatarView = activity.findViewById(R.id.account_avatar);
+ assertThat(avatarView.getVisibility()).isNotEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ @Config(qualifiers = "mcc999")
+ public void launch_configEnabled_shouldShowAvatar() {
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+
+ final View avatarView = activity.findViewById(R.id.account_avatar);
+ assertThat(avatarView.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ @Config(qualifiers = "mcc999")
+ public void launch_LowRamDevice_shouldHideAvatar() {
+ final ShadowActivityManager activityManager = Shadow.extract(
+ RuntimeEnvironment.application.getSystemService(ActivityManager.class));
+ activityManager.setIsLowRamDevice(true);
+
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+
+ final View avatarView = activity.findViewById(R.id.account_avatar);
+ assertThat(avatarView.getVisibility()).isNotEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void showHomepageWithSuggestion_showSuggestion() {
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+ final View viewRoot = activity.findViewById(R.id.settings_homepage_container);
+ final View suggestionTile = activity.findViewById(R.id.suggestion_content);
+
+ activity.showHomepageWithSuggestion(true);
+
+ assertThat(viewRoot.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(suggestionTile.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void showHomepageWithSuggestion_hideSuggestion() {
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+ final View viewRoot = activity.findViewById(R.id.settings_homepage_container);
+ final View suggestionTile = activity.findViewById(R.id.suggestion_content);
+
+ activity.showHomepageWithSuggestion(false);
+
+ assertThat(viewRoot.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(suggestionTile.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void showHomepageWithSuggestion_callTwice_shouldKeepPreviousVisibility() {
+ final SettingsHomepageActivity activity = Robolectric.buildActivity(
+ SettingsHomepageActivity.class).create().get();
+ final View suggestionTile = activity.findViewById(R.id.suggestion_content);
+
+ activity.showHomepageWithSuggestion(false);
+ activity.showHomepageWithSuggestion(true);
+
+ assertThat(suggestionTile.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
@Config(shadows = {
BatteryFixSliceTest.ShadowBatteryTipLoader.class
})
@@ -114,4 +194,13 @@
assertThat(paramCaptor.getValue().privateFlags
& SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS).isEqualTo(0);
}
+
+ @Implements(SuggestionFeatureProviderImpl.class)
+ public static class ShadowSuggestionFeatureProviderImpl {
+
+ @Implementation
+ public Class<? extends Fragment> getContextualSuggestionFragment() {
+ return Fragment.class;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
deleted file mode 100644
index 180f3e2..0000000
--- a/tests/robotests/src/com/android/settings/location/LocationFooterPreferenceControllerTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2018 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 static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.doNothing;
-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.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.location.LocationManager;
-import android.os.Bundle;
-
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@RunWith(RobolectricTestRunner.class)
-public class LocationFooterPreferenceControllerTest {
-
- @Mock
- private PreferenceCategory mPreferenceCategory;
- @Mock
- private PackageManager mPackageManager;
- @Mock
- private Resources mResources;
- private LocationFooterPreferenceController mController;
- private static final int TEST_RES_ID = 1234;
- private static final String TEST_TEXT = "text";
-
- @Before
- public void setUp() throws NameNotFoundException {
- MockitoAnnotations.initMocks(this);
- Context context = spy(RuntimeEnvironment.application);
- when(context.getPackageManager()).thenReturn(mPackageManager);
- when(mPreferenceCategory.getContext()).thenReturn(context);
- mController = spy(new LocationFooterPreferenceController(context, "key"));
- when(mPackageManager.getResourcesForApplication(any(ApplicationInfo.class)))
- .thenReturn(mResources);
- when(mResources.getString(TEST_RES_ID)).thenReturn(TEST_TEXT);
- doNothing().when(mPreferenceCategory).removeAll();
- }
-
- @Test
- public void isAvailable_hasValidFooter_returnsTrue() {
- final List<ResolveInfo> testResolveInfos = new ArrayList<>();
- testResolveInfos.add(
- getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
- when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
- .thenReturn(testResolveInfos);
-
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void isAvailable_noSystemApp_returnsFalse() {
- final List<ResolveInfo> testResolveInfos = new ArrayList<>();
- testResolveInfos.add(
- getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
- when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
- .thenReturn(testResolveInfos);
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_noRequiredMetadata_returnsFalse() {
- final List<ResolveInfo> testResolveInfos = new ArrayList<>();
- testResolveInfos.add(
- getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ false));
- when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
- .thenReturn(testResolveInfos);
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void updateState_addPreferences() {
- final List<ResolveInfo> testResolveInfos = new ArrayList<>();
- testResolveInfos.add(
- getTestResolveInfo(/*isSystemApp*/ true, /*hasRequiredMetadata*/ true));
- when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
- .thenReturn(testResolveInfos);
- mController.updateState(mPreferenceCategory);
- ArgumentCaptor<Preference> pref = ArgumentCaptor.forClass(Preference.class);
- verify(mPreferenceCategory).addPreference(pref.capture());
- assertThat(pref.getValue().getTitle()).isEqualTo(TEST_TEXT);
- }
-
- @Test
- public void updateState_notSystemApp_ignore() {
- final List<ResolveInfo> testResolveInfos = new ArrayList<>();
- testResolveInfos.add(
- getTestResolveInfo(/*isSystemApp*/ false, /*hasRequiredMetadata*/ true));
- when(mPackageManager.queryBroadcastReceivers(any(Intent.class), anyInt()))
- .thenReturn(testResolveInfos);
- mController.updateState(mPreferenceCategory);
- verify(mPreferenceCategory, never()).addPreference(any(Preference.class));
- }
-
- /**
- * Returns a ResolveInfo object for testing
- * @param isSystemApp If true, the application is a system app.
- * @param hasRequiredMetaData If true, the broadcast receiver has a valid value for
- * {@link LocationManager#METADATA_SETTINGS_FOOTER_STRING}
- */
- private ResolveInfo getTestResolveInfo(boolean isSystemApp, boolean hasRequiredMetaData) {
- ResolveInfo testResolveInfo = new ResolveInfo();
- ApplicationInfo testAppInfo = new ApplicationInfo();
- if (isSystemApp) {
- testAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
- }
- ActivityInfo testActivityInfo = new ActivityInfo();
- testActivityInfo.name = "TestActivityName";
- testActivityInfo.packageName = "TestPackageName";
- testActivityInfo.applicationInfo = testAppInfo;
- if (hasRequiredMetaData) {
- testActivityInfo.metaData = new Bundle();
- testActivityInfo.metaData.putInt(
- LocationManager.METADATA_SETTINGS_FOOTER_STRING, TEST_RES_ID);
- }
- testResolveInfo.activityInfo = testActivityInfo;
- return testResolveInfo;
- }
-}
diff --git a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java
index d7c0ffe..c1377ff 100644
--- a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java
@@ -44,6 +44,7 @@
import android.os.PowerManager;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.FeatureFlagUtils;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
@@ -131,6 +132,7 @@
mNetworkProviderSettings.mResetInternetPreference = mResetInternetPreference;
mNetworkProviderSettings.mAirplaneModeMsgPreference = mAirplaneModeMsgPreference;
mNetworkProviderSettings.mAirplaneModeEnabler = mAirplaneModeEnabler;
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, false);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java
index 660b4e1..15bc598 100644
--- a/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/RecentConversationsPreferenceControllerTest.java
@@ -47,8 +47,10 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
+import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
import org.junit.Test;
@@ -105,9 +107,9 @@
ps.addPreference(outerContainer);
ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
- new NotificationChannel("hi", "hi", 4),
- new NotificationChannelGroup("hi", "hi"), 7,
- true);
+ new NotificationChannel("hi", "hi", 4),
+ new NotificationChannelGroup("hi", "hi"), 7,
+ false);
ArrayList<ConversationChannel> list = new ArrayList<>();
list.add(ccw);
@@ -124,19 +126,19 @@
ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
new NotificationChannel("hi", "hi", 4),
new NotificationChannelGroup("hi", "hi"), 7,
- true);
+ false);
ConversationChannel ccw2 = new ConversationChannel(mock(ShortcutInfo.class), 6,
new NotificationChannel("hi", "hi", 0),
new NotificationChannelGroup("hi", "hi"), 7,
- true);
+ false);
NotificationChannelGroup blockedGroup = new NotificationChannelGroup("hi", "hi");
blockedGroup.setBlocked(true);
ConversationChannel ccw3 = new ConversationChannel(mock(ShortcutInfo.class), 6,
new NotificationChannel("hi", "hi", 4),
blockedGroup, 7,
- true);
+ false);
ArrayList<ConversationChannel> list = new ArrayList<>();
list.add(ccw);
@@ -198,7 +200,6 @@
new NotificationChannelGroup("hi", "group"), 7,
true);
-
Intent intent = mController.getSubSettingLauncher(ccw, "title").toIntent();
Bundle extras = intent.getExtras();
@@ -258,6 +259,51 @@
}
@Test
+ public void testRemoveConversations() throws Exception {
+ ShortcutInfo si = mock(ShortcutInfo.class);
+ when(si.getId()).thenReturn("person");
+ when(si.getPackage()).thenReturn("pkg");
+ ConversationChannel ccw = new ConversationChannel(si, 6,
+ new NotificationChannel("hi", "hi", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ false);
+
+ ConversationChannel ccw2 = new ConversationChannel(si, 6,
+ new NotificationChannel("bye", "bye", 4),
+ new NotificationChannelGroup("hi", "group"), 7,
+ true);
+
+ PreferenceCategory group = new PreferenceCategory(mContext);
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+ screen.addPreference(group);
+
+ RecentConversationPreference pref = mController.createConversationPref(
+ new PreferenceCategory(mContext), ccw, 100);
+ final View view = View.inflate(mContext, pref.getLayoutResource(), null);
+ PreferenceViewHolder holder = spy(PreferenceViewHolder.createInstanceForTests(view));
+ View delete = View.inflate(mContext, pref.getSecondTargetResId(), null);
+ when(holder.findViewById(pref.getClearId())).thenReturn(delete);
+ group.addPreference(pref);
+
+ RecentConversationPreference pref2 = mController.createConversationPref(
+ new PreferenceCategory(mContext), ccw2, 100);
+ final View view2 = View.inflate(mContext, pref2.getLayoutResource(), null);
+ PreferenceViewHolder holder2 = spy(PreferenceViewHolder.createInstanceForTests(view2));
+ View delete2 = View.inflate(mContext, pref2.getSecondTargetResId(), null);
+ when(holder2.findViewById(pref.getClearId())).thenReturn(delete2);
+ group.addPreference(pref2);
+
+ LayoutPreference clearAll = mController.getClearAll(group);
+ group.addPreference(clearAll);
+
+ clearAll.findViewById(R.id.conversation_settings_clear_recents).performClick();
+
+ verify(mPs).removeAllRecentConversations();
+ assertThat((Preference) group.findPreference("hi:person")).isNull();
+ assertThat((Preference) group.findPreference("bye:person")).isNotNull();
+ }
+
+ @Test
public void testNonremoveableConversation() throws Exception {
ShortcutInfo si = mock(ShortcutInfo.class);
when(si.getId()).thenReturn("person");
@@ -272,4 +318,24 @@
new PreferenceCategory(mContext), ccw, 100);
assertThat(pref.hasClearListener()).isFalse();
}
+
+ @Test
+ public void testPopulateList_onlyNonremoveableConversations() {
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ PreferenceScreen ps = preferenceManager.createPreferenceScreen(mContext);
+ PreferenceCategory outerContainer = spy(new PreferenceCategory(mContext));
+ ps.addPreference(outerContainer);
+
+ ConversationChannel ccw = new ConversationChannel(mock(ShortcutInfo.class), 6,
+ new NotificationChannel("hi", "hi", 4),
+ new NotificationChannelGroup("hi", "hi"), 7,
+ true /* hasactivenotifs */);
+
+ ArrayList<ConversationChannel> list = new ArrayList<>();
+ list.add(ccw);
+
+ mController.populateList(list, outerContainer);
+ // one for the preference, none for 'clear all'
+ verify(outerContainer, times(1)).addPreference(any());
+ }
}
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/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index a48b3eb..b7e6ffd 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -30,6 +30,7 @@
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -42,6 +43,7 @@
import com.android.settings.panel.PanelFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
+import com.android.settings.security.SecuritySettingsFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
import com.android.settings.users.UserFeatureProvider;
import com.android.settings.wifi.WifiTrackerLibProvider;
@@ -58,6 +60,7 @@
public final SupportFeatureProvider supportFeatureProvider;
public final MetricsFeatureProvider metricsFeatureProvider;
public final BatteryStatusFeatureProvider batteryStatusFeatureProvider;
+ public final BatterySettingsFeatureProvider batterySettingsFeatureProvider;
public final PowerUsageFeatureProvider powerUsageFeatureProvider;
public final DashboardFeatureProvider dashboardFeatureProvider;
public final DockUpdaterFeatureProvider dockUpdaterFeatureProvider;
@@ -81,6 +84,7 @@
public WifiTrackerLibProvider wifiTrackerLibProvider;
public ExtraAppInfoFeatureProvider extraAppInfoFeatureProvider;
+ public SecuritySettingsFeatureProvider securitySettingsFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -106,6 +110,7 @@
supportFeatureProvider = mock(SupportFeatureProvider.class);
metricsFeatureProvider = mock(MetricsFeatureProvider.class);
batteryStatusFeatureProvider = mock(BatteryStatusFeatureProvider.class);
+ batterySettingsFeatureProvider = mock(BatterySettingsFeatureProvider.class);
powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
dockUpdaterFeatureProvider = mock(DockUpdaterFeatureProvider.class);
@@ -127,6 +132,7 @@
mFaceFeatureProvider = mock(FaceFeatureProvider.class);
wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class);
+ securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
}
@Override
@@ -150,6 +156,11 @@
}
@Override
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ return batterySettingsFeatureProvider;
+ }
+
+ @Override
public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
return powerUsageFeatureProvider;
}
@@ -248,4 +259,9 @@
public ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider() {
return extraAppInfoFeatureProvider;
}
+
+ @Override
+ public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() {
+ return securitySettingsFeatureProvider;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index 3494c63..c542c55 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -34,6 +34,7 @@
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
+import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -61,6 +62,7 @@
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.After;
import org.junit.Before;
@@ -111,6 +113,8 @@
private RestrictedPreference mAddGuestPreference;
@Mock
private UserManager mUserManager;
+ @Mock
+ private MetricsFeatureProvider mMetricsFeatureProvider;
private FragmentActivity mActivity;
private Context mContext;
@@ -134,6 +138,7 @@
ReflectionHelpers.setField(mFragment, "mUserCaps", mUserCapabilities);
ReflectionHelpers.setField(mFragment, "mDefaultIconDrawable", mDefaultIconDrawable);
ReflectionHelpers.setField(mFragment, "mAddingUser", false);
+ ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider", mMetricsFeatureProvider);
doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class);
@@ -616,6 +621,7 @@
.isEqualTo(createdGuest.id);
assertThat(arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false))
.isEqualTo(true);
+ verify(mMetricsFeatureProvider).action(any(), eq(SettingsEnums.ACTION_USER_GUEST_ADD));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
index dc5ccb4..6cbb19d 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiNoInternetDialogTest.java
@@ -44,6 +44,7 @@
import com.android.settings.R;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -87,6 +88,7 @@
Robolectric.setupActivity(WifiNoInternetDialog.class);
}
+ @Ignore
@Test
public void setupPromptUnvalidated_shouldShowNoInternetAccessRemember() {
setupActivityWithAction(ACTION_PROMPT_UNVALIDATED, FAKE_URL);
@@ -98,6 +100,7 @@
mActivity.getString(R.string.no_internet_access_remember));
}
+ @Ignore
@Test
public void setupPromptPartialConnectivity_shouldShowNoInternetAccessRemember() {
setupActivityWithAction(ACTION_PROMPT_PARTIAL_CONNECTIVITY, FAKE_URL);
@@ -109,6 +112,7 @@
mActivity.getString(R.string.no_internet_access_remember));
}
+ @Ignore
@Test
public void setupPromptLostValidationAction_shouldShowLostInternetAccessPersist() {
setupActivityWithAction(ACTION_PROMPT_LOST_VALIDATION, FAKE_URL);
@@ -120,6 +124,7 @@
mActivity.getString(R.string.lost_internet_access_persist));
}
+ @Ignore
@Test
public void clickPositiveButton_whenPromptUnvalidated_shouldCallSetAcceptUnvalidated() {
setupActivityWithAction(ACTION_PROMPT_UNVALIDATED, FAKE_URL);
@@ -131,6 +136,7 @@
verify(mConnectivityManager).setAcceptUnvalidated(any(Network.class), eq(true), eq(false));
}
+ @Ignore
@Test
public void positiveButton_withPartialConnectivity_shouldCallSetAcceptPartialConnectivity() {
setupActivityWithAction(ACTION_PROMPT_PARTIAL_CONNECTIVITY, FAKE_URL);
@@ -143,6 +149,7 @@
eq(false));
}
+ @Ignore
@Test
public void positiveButton_withLostValidation_shouldCallSetAvoidUnvalidated() {
setupActivityWithAction(ACTION_PROMPT_LOST_VALIDATION, FAKE_URL);
@@ -154,6 +161,7 @@
verify(mConnectivityManager).setAvoidUnvalidated(any(Network.class));
}
+ @Ignore
@Test
public void destroyWithNoClick_inPartialConnectivity_shouldCallSetAcceptPartialConnectivity() {
setupActivityWithAction(ACTION_PROMPT_PARTIAL_CONNECTIVITY, FAKE_URL);
@@ -167,6 +175,7 @@
eq(false));
}
+ @Ignore
@Test
public void destroyWithNoClick_whenUnvalidated_shouldCallSetAcceptUnvalidated() {
setupActivityWithAction(ACTION_PROMPT_UNVALIDATED, FAKE_URL);
@@ -179,6 +188,7 @@
verify(mConnectivityManager).setAcceptUnvalidated(any(Network.class), eq(false), eq(false));
}
+ @Ignore
@Test
public void networkCallbackOnLost_shouldFinish() {
setupActivityWithAction(ACTION_PROMPT_UNVALIDATED, FAKE_URL);
@@ -192,6 +202,7 @@
verify(mActivity).finish();
}
+ @Ignore
@Test
public void networkCallbackOnCapabilitiesChanged_shouldFinish() {
setupActivityWithAction(ACTION_PROMPT_UNVALIDATED, FAKE_URL);
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
index 7eba586..1f5abd3 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiSettingsTest.java
@@ -43,6 +43,7 @@
import android.os.PowerManager;
import android.os.UserManager;
import android.provider.Settings;
+import android.util.FeatureFlagUtils;
import android.view.ContextMenu;
import android.view.View;
@@ -105,6 +106,7 @@
mWifiSettings.mConfigureWifiSettingsPreference = new Preference(mContext);
mWifiSettings.mWifiPickerTracker = mMockWifiPickerTracker;
mWifiSettings.mWifiManager = mWifiManager;
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL, false);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
index c5f2a7f..3e0e22f 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -52,6 +52,7 @@
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.Uri;
+import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiInfo;
@@ -83,6 +84,7 @@
import com.android.wifitrackerlib.NetworkDetailsTracker;
import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
+import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
import org.junit.Before;
import org.junit.Test;
@@ -189,6 +191,8 @@
@Mock
private Preference mMockDnsPref;
@Mock
+ private Preference mMockTypePref;
+ @Mock
private PreferenceCategory mMockIpv6Category;
@Mock
private Preference mMockIpv6AddressesPref;
@@ -390,6 +394,8 @@
.thenReturn(mMockSubnetPref);
when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_DNS_PREF))
.thenReturn(mMockDnsPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_WIFI_TYPE_PREF))
+ .thenReturn(mMockTypePref);
when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_CATEGORY))
.thenReturn(mMockIpv6Category);
when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_ADDRESSES_PREF))
@@ -931,6 +937,44 @@
}
@Test
+ public void onConnectedNetwork_getKnownNetworkType_visibleWifiTypePref() {
+ setUpForConnectedNetwork();
+ setUpSpyController();
+ setWifiType(ScanResult.WIFI_STANDARD_11AX);
+
+ displayAndResume();
+
+ verify(mMockTypePref).setSummary(R.string.wifi_type_11AX);
+ verify(mMockTypePref).setVisible(true);
+ }
+
+ @Test
+ public void onConnectedNetwork_getUnKnownNetworkType_invisibleWifiTypePref() {
+ setUpForConnectedNetwork();
+ setUpSpyController();
+ setWifiType(ScanResult.WIFI_STANDARD_UNKNOWN);
+
+ displayAndResume();
+
+ verify(mMockTypePref).setVisible(false);
+ }
+
+ @Test
+ public void onDisconnectedNetwork_resumeUI_invisibleWifiTypePref() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockTypePref).setVisible(false);
+ }
+
+ private void setWifiType(int type) {
+ ConnectedInfo connectedInfo = new ConnectedInfo();
+ connectedInfo.wifiStandard = type;
+ when(mMockWifiEntry.getConnectedInfo()).thenReturn(connectedInfo);
+ }
+
+ @Test
public void noCurrentNetwork_shouldNotFinishActivityForConnectedNetwork() {
setUpForConnectedNetwork();
setUpSpyController();
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
index e0fb578..66b5bcb 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
@@ -24,9 +24,9 @@
import static org.mockito.ArgumentMatchers.eq;
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;
import android.app.settings.SettingsEnums;
import android.view.Menu;
@@ -36,12 +36,17 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.wifitrackerlib.NetworkDetailsTracker;
+import com.android.wifitrackerlib.WifiEntry;
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;
@@ -52,11 +57,22 @@
final String TEST_PREFERENCE_KEY = "TEST_PREFERENCE_KEY";
+ @Mock
+ WifiEntry mWifiEntry;
+ @Mock
+ NetworkDetailsTracker mNetworkDetailsTracker;
+ @Mock
+ Menu mMenu;
private WifiNetworkDetailsFragment2 mFragment;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mWifiEntry).when(mNetworkDetailsTracker).getWifiEntry();
+ doReturn(true).when(mWifiEntry).isSaved();
+
mFragment = new WifiNetworkDetailsFragment2();
+ mFragment.mNetworkDetailsTracker = mNetworkDetailsTracker;
}
@Test
@@ -77,16 +93,24 @@
@Test
public void onCreateOptionsMenu_shouldSetCorrectIcon() {
- final Menu menu = mock(Menu.class);
final MenuItem menuItem = mock(MenuItem.class);
- doReturn(menuItem).when(menu).add(anyInt(), eq(Menu.FIRST), anyInt(), anyInt());
+ doReturn(menuItem).when(mMenu).add(anyInt(), eq(Menu.FIRST), anyInt(), anyInt());
- mFragment.onCreateOptionsMenu(menu, mock(MenuInflater.class));
+ mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class));
verify(menuItem).setIcon(com.android.internal.R.drawable.ic_mode_edit);
}
@Test
+ public void onCreateOptionsMenu_isNotSavedNetwork_shouldNotAddEditMenu() {
+ doReturn(false).when(mWifiEntry).isSaved();
+
+ mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class));
+
+ verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), eq(R.string.wifi_modify));
+ }
+
+ @Test
public void refreshPreferences_controllerShouldUpdateStateAndDisplayPreference() {
final FakeFragment fragment = spy(new FakeFragment());
final PreferenceScreen screen = mock(PreferenceScreen.class);
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 a423071..c1d6c16 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherFooterPreferenceControllerTest.java
@@ -17,6 +17,7 @@
package com.android.settings.wifi.tether;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -57,7 +58,8 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+ doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
+ doReturn(mTetheringManager).when(mContext).getSystemService(TetheringManager.class);
when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager);
when(mTetheringManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
mController = new WifiTetherFooterPreferenceController(mContext);
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
index 8f8477f..bb61eb9 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherPasswordPreferenceControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -75,9 +76,9 @@
.setPassphrase(INITIAL_PASSWORD, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
.build();
- when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
+ doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
when(mWifiManager.getSoftApConfiguration()).thenReturn(mConfig);
- when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager);
+ doReturn(mTetheringManager).when(mContext).getSystemService(TetheringManager.class);
when(mTetheringManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources());
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
index 147cd64..2d88722 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -67,8 +68,8 @@
MockitoAnnotations.initMocks(this);
mPreference = new WifiTetherSsidPreference(RuntimeEnvironment.application);
- when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifiManager);
- when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager);
+ doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
+ doReturn(mTetheringManager).when(mContext).getSystemService(TetheringManager.class);
when(mTetheringManager.getTetherableWifiRegexs()).thenReturn(new String[]{"1", "2"});
when(mContext.getResources()).thenReturn(RuntimeEnvironment.application.getResources());
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
diff --git a/tests/uitests/AndroidManifest.xml b/tests/uitests/AndroidManifest.xml
index dc6fc15..1072754 100644
--- a/tests/uitests/AndroidManifest.xml
+++ b/tests/uitests/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES"/>
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index b55a788..5582ff4 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -32,6 +32,10 @@
"platform-test-annotations",
"truth-prebuilt",
"ub-uiautomator",
+ "SettingsLibSettingsSpinner",
+ "SettingsLibUsageProgressBarPreference",
+ "SettingsLibTwoTargetPreference",
+ "SettingsLibMainSwitchPreference",
],
// Include all test java files.
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 616e6a9..7ef7ae1 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
diff --git a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
index 8dac893..22d0b29 100644
--- a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
@@ -24,7 +24,6 @@
import android.content.Context;
import android.content.res.Resources;
-import android.hardware.display.ColorDisplayManager;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
@@ -83,8 +82,8 @@
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
mPreferenceController.onPreferenceChange(/* preference= */ null, 20);
assertThat(
- mContext.getSystemService(
- ColorDisplayManager.class).getReduceBrightColorsStrength())
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0))
.isEqualTo(20);
}
diff --git a/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java
index cf4c53e..a34e634 100644
--- a/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/HibernatedAppsPreferenceControllerTest.java
@@ -23,9 +23,18 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.apphibernation.AppHibernationManager;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.provider.DeviceConfig;
import androidx.test.core.app.ApplicationProvider;
@@ -34,23 +43,40 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
-/**
- * TODO(b/181172051): test getNumberHibernated() when the API implemented
- */
+import java.util.Arrays;
+
@RunWith(AndroidJUnit4.class)
public class HibernatedAppsPreferenceControllerTest {
+ public static final String HIBERNATED_PACKAGE_NAME = "hibernated_package";
+ public static final String AUTO_REVOKED_PACKAGE_NAME = "auto_revoked_package";
+ public static final String PERMISSION = "permission";
+ @Mock
+ PackageManager mPackageManager;
+ @Mock
+ AppHibernationManager mAppHibernationManager;
private static final String KEY = "key";
private Context mContext;
private HibernatedAppsPreferenceController mController;
+ private PackageInfo mHibernatedPackage;
+ private PackageInfo mAutoRevokedPackage;
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
DeviceConfig.setProperty(NAMESPACE_APP_HIBERNATION, PROPERTY_APP_HIBERNATION_ENABLED,
"true", false);
mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(AppHibernationManager.class))
+ .thenReturn(mAppHibernationManager);
mController = new HibernatedAppsPreferenceController(mContext, KEY);
+ mHibernatedPackage =
+ getHibernatedPackage(mAppHibernationManager, mPackageManager, mContext);
+ mAutoRevokedPackage = getAutoRevokedPackage(mPackageManager, mContext);
}
@Test
@@ -60,4 +86,38 @@
assertThat((mController).getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
+
+ @Test
+ public void getSummary_shouldReturnCorrectly() {
+ when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(
+ Arrays.asList(mHibernatedPackage, mAutoRevokedPackage, new PackageInfo()));
+ when(mContext.getResources()).thenReturn(mock(Resources.class));
+ final int totalHibernated = 2;
+
+ mController.getSummary();
+ verify(mContext.getResources()).getQuantityString(
+ anyInt(), eq(totalHibernated), eq(totalHibernated));
+ }
+
+ private static PackageInfo getHibernatedPackage(
+ AppHibernationManager apm, PackageManager pm, Context context) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = HIBERNATED_PACKAGE_NAME;
+ pi.requestedPermissions = new String[] {PERMISSION};
+ when(apm.getHibernatingPackagesForUser()).thenReturn(Arrays.asList(pi.packageName));
+ when(pm.getPermissionFlags(
+ pi.requestedPermissions[0], pi.packageName, context.getUser()))
+ .thenReturn(PackageManager.FLAG_PERMISSION_AUTO_REVOKED);
+ return pi;
+ }
+
+ private static PackageInfo getAutoRevokedPackage(PackageManager pm, Context context) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = AUTO_REVOKED_PACKAGE_NAME;
+ pi.requestedPermissions = new String[] {PERMISSION};
+ when(pm.getPermissionFlags(
+ pi.requestedPermissions[0], pi.packageName, context.getUser()))
+ .thenReturn(PackageManager.FLAG_PERMISSION_AUTO_REVOKED);
+ return pi;
+ }
}
diff --git a/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java
index e27942a..65ffe4b 100644
--- a/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/appinfo/HibernationSwitchPreferenceControllerTest.java
@@ -70,9 +70,9 @@
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
- when(mPackageManager.getPackageUidAsUser(eq(VALID_PACKAGE_NAME), anyInt()))
+ when(mPackageManager.getPackageUid(eq(VALID_PACKAGE_NAME), anyInt()))
.thenReturn(PACKAGE_UID);
- when(mPackageManager.getPackageUidAsUser(eq(INVALID_PACKAGE_NAME), anyInt()))
+ when(mPackageManager.getPackageUid(eq(INVALID_PACKAGE_NAME), anyInt()))
.thenThrow(new PackageManager.NameNotFoundException());
when(mPackageManager.getTargetSdkVersion(eq(EXEMPTED_PACKAGE_NAME)))
.thenReturn(android.os.Build.VERSION_CODES.Q);
diff --git a/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java
index 216658f..25d9893 100644
--- a/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/applications/autofill/PasswordsPreferenceControllerTest.java
@@ -21,21 +21,26 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.ComponentName;
import android.content.Context;
import android.os.Looper;
import android.service.autofill.AutofillServiceInfo;
+import androidx.lifecycle.Lifecycle;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.collect.Lists;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -100,11 +105,15 @@
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(0);
}
+ @Ignore("TODO: Fix the test to handle the service binding.")
@Test
+ @UiThreadTest
public void displayPreference_withPasswords_addsPreference() {
AutofillServiceInfo service = createServiceWithPasswords();
PasswordsPreferenceController controller =
createControllerWithServices(Lists.newArrayList(service));
+ controller.onCreate(() -> mock(Lifecycle.class));
+
controller.displayPreference(mScreen);
assertThat(mPasswordsPreferenceCategory.getPreferenceCount()).isEqualTo(1);
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/storage/StorageEntryTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
new file mode 100644
index 0000000..3cd3539
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
@@ -0,0 +1,319 @@
+/*
+ * 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 isPublic_prublicVolume_shouldReturnTrue() {
+ final VolumeInfo publicVolumeInfo = mock(VolumeInfo.class);
+ final StorageEntry publicStorage = new StorageEntry(mContext, publicVolumeInfo);
+ when(publicVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
+
+ assertThat(publicStorage.isPublic()).isTrue();
+ }
+
+ @Test
+ public void isStub_stubVolume_shouldReturnTrue() {
+ final VolumeInfo stubVolumeInfo = mock(VolumeInfo.class);
+ final StorageEntry stubStorage = new StorageEntry(mContext, stubVolumeInfo);
+ when(stubVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_STUB);
+
+ assertThat(stubStorage.isStub()).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/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index 4be2ae6..9b42951 100644
--- a/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -94,7 +94,7 @@
final BatteryTipPolicy batteryTipPolicy = new BatteryTipPolicy(mContext);
assertThat(batteryTipPolicy.batteryTipEnabled).isTrue();
- assertThat(batteryTipPolicy.summaryEnabled).isTrue();
+ assertThat(batteryTipPolicy.summaryEnabled).isFalse();
assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
diff --git a/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java b/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
index 6ddef5d..879b9f7 100644
--- a/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/EraseEuiccDataControllerTest.java
@@ -18,8 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.spy;
-
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
diff --git a/tests/unit/src/com/android/settings/network/InternetUpdaterTest.java b/tests/unit/src/com/android/settings/network/InternetUpdaterTest.java
index 94456fe..3e9cdc7 100644
--- a/tests/unit/src/com/android/settings/network/InternetUpdaterTest.java
+++ b/tests/unit/src/com/android/settings/network/InternetUpdaterTest.java
@@ -38,6 +38,7 @@
import android.content.Context;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
import android.net.wifi.WifiManager;
import androidx.lifecycle.Lifecycle;
@@ -107,57 +108,80 @@
}
@Test
- public void update_apmOnWifiOff_getInternetApm() {
+ public void updateInternetAvailable_wifiConnectedAndNoValidated_internetUnavailable() {
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addTransportType(TRANSPORT_WIFI);
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ mInternetUpdater.updateInternetAvailable(networkCapabilities);
+
+ assertThat(mInternetUpdater.mInternetAvailable).isEqualTo(false);
+ }
+
+ @Test
+ public void updateInternetAvailable_wifiConnectedAndValidated_internetAvailable() {
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addTransportType(TRANSPORT_WIFI);
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+
+ mInternetUpdater.updateInternetAvailable(networkCapabilities);
+
+ assertThat(mInternetUpdater.mInternetAvailable).isEqualTo(true);
+ }
+
+ @Test
+ public void updateInternetType_apmOnWifiOff_getInternetApm() {
doReturn(true).when(mAirplaneModeEnabler).isAirplaneModeOn();
doReturn(WifiManager.WIFI_STATE_DISABLED).when(mWifiManager).getWifiState();
mInternetUpdater.mInternetAvailable = false;
- mInternetUpdater.update();
+ mInternetUpdater.updateInternetType();
assertThat(mInternetUpdater.getInternetType()).isEqualTo(INTERNET_OFF);
}
@Test
- public void update_apmOnWifiOnNotConnected_getInternetNetworksAvailable() {
+ public void updateInternetType_apmOnWifiOnNotConnected_getInternetNetworksAvailable() {
doReturn(true).when(mAirplaneModeEnabler).isAirplaneModeOn();
doReturn(WifiManager.WIFI_STATE_ENABLED).when(mWifiManager).getWifiState();
mInternetUpdater.mInternetAvailable = false;
- mInternetUpdater.update();
+ mInternetUpdater.updateInternetType();
assertThat(mInternetUpdater.getInternetType()).isEqualTo(INTERNET_NETWORKS_AVAILABLE);
}
@Test
- public void update_apmOnWifiConnected_getInternetWifi() {
+ public void updateInternetType_apmOnWifiConnected_getInternetWifi() {
doReturn(true).when(mAirplaneModeEnabler).isAirplaneModeOn();
doReturn(true).when(mWifiManager).isWifiEnabled();
mInternetUpdater.mInternetAvailable = true;
mInternetUpdater.mTransport = TRANSPORT_WIFI;
- mInternetUpdater.update();
+ mInternetUpdater.updateInternetType();
assertThat(mInternetUpdater.getInternetType()).isEqualTo(INTERNET_WIFI);
}
@Test
- public void update_apmOnCellularConnected_getInternetCellular() {
+ public void updateInternetType_apmOnCellularConnected_getInternetCellular() {
doReturn(true).when(mAirplaneModeEnabler).isAirplaneModeOn();
mInternetUpdater.mInternetAvailable = true;
mInternetUpdater.mTransport = TRANSPORT_CELLULAR;
- mInternetUpdater.update();
+ mInternetUpdater.updateInternetType();
assertThat(mInternetUpdater.getInternetType()).isEqualTo(INTERNET_CELLULAR);
}
@Test
- public void update_apmOnEthernetConnected_getInternetEthernet() {
+ public void updateInternetType_apmOnEthernetConnected_getInternetEthernet() {
doReturn(true).when(mAirplaneModeEnabler).isAirplaneModeOn();
mInternetUpdater.mInternetAvailable = true;
mInternetUpdater.mTransport = TRANSPORT_ETHERNET;
- mInternetUpdater.update();
+ mInternetUpdater.updateInternetType();
assertThat(mInternetUpdater.getInternetType()).isEqualTo(INTERNET_ETHERNET);
}
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
index ac2e24d..9c5c88b 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
@@ -33,6 +33,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.Uri;
+import android.net.wifi.WifiManager;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
@@ -90,6 +91,8 @@
private SubscriptionInfo mDefaultDataSubscriptionInfo;
@Mock
private Drawable mDrawableWithSignalStrength;
+ @Mock
+ private WifiManager mWifiManager;
@Before
public void setUp() {
@@ -102,13 +105,23 @@
when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
mCarrierConfigManager);
when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+ mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+ when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
TestCustomSliceable testCustomSliceable = new TestCustomSliceable();
mProviderModelSliceHelper = new MockProviderModelSliceHelper(mContext, testCustomSliceable);
+
+ final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+ mDefaultDataSubscriptionInfo);
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+ Arrays.asList(mDefaultDataSubscriptionInfo));
}
@Test
@@ -155,20 +168,9 @@
String expectDisplayName = "Name1";
CharSequence expectedSubtitle = Html.fromHtml("5G", Html.FROM_HTML_MODE_LEGACY);
String networkType = "5G";
-
- final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
- when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
- when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
- mDefaultDataSubscriptionInfo);
- when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
- Arrays.asList(mDefaultDataSubscriptionInfo));
-
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
- mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
+ mTelephonyManager.DATA_CONNECTED, true);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-
ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
networkType);
@@ -178,7 +180,7 @@
}
@Test
- public void createCarrierRow_hasDdsAndActiveNetworkIsCellular_verifyTitleAndSummary() {
+ public void createCarrierRow_wifiOnhasDdsAndActiveNetworkIsCellular_verifyTitleAndSummary() {
String expectDisplayName = "Name1";
String networkType = "5G";
String connectedText = ResourcesUtils.getResourcesString(mContext,
@@ -186,16 +188,27 @@
CharSequence expectedSubtitle = Html.fromHtml(ResourcesUtils.getResourcesString(mContext,
"preference_summary_default_combination", connectedText, networkType),
Html.FROM_HTML_MODE_LEGACY);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
+ mTelephonyManager.DATA_CONNECTED, true);
+ addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
- final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
- when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
- when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
- mDefaultDataSubscriptionInfo);
- when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
- Arrays.asList(mDefaultDataSubscriptionInfo));
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
- mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+ ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
+ networkType);
+
+ assertThat(testRowBuild.getTitle()).isEqualTo(expectDisplayName);
+ assertThat(testRowBuild.getSubtitle()).isEqualTo(expectedSubtitle);
+ }
+
+ @Test
+ public void createCarrierRow_noNetworkAvailable_verifyTitleAndSummary() {
+ String expectDisplayName = "Name1";
+ CharSequence expectedSubtitle = Html.fromHtml(
+ ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection"),
+ Html.FROM_HTML_MODE_LEGACY);
+ String networkType = "";
+
+ mockConnections(true, ServiceState.STATE_OUT_OF_SERVICE, expectDisplayName,
+ mTelephonyManager.DATA_DISCONNECTED, false);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
@@ -207,53 +220,40 @@
@Test
public void isNoCarrierData_mobileDataOnAndNoData_returnTrue() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, "",
+ mTelephonyManager.DATA_DISCONNECTED, true);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
}
@Test
public void isNoCarrierData_mobileDataOffAndOutOfService_returnTrue() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "",
+ mTelephonyManager.DATA_DISCONNECTED, true);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
}
@Test
public void isNoCarrierData_mobileDataOnAndDataConnected_returnFalse() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+ true);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
}
@Test
public void isNoCarrierData_mobileDataOffAndVoiceIsInService_returnFalse() {
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ mockConnections(false, ServiceState.STATE_IN_SERVICE, "",
+ mTelephonyManager.DATA_DISCONNECTED, true);
assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
}
@Test
public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable {
- when(mTelephonyManager.isDataEnabled()).thenReturn(false);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
- int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
- mDefaultDataSubscriptionInfo);
+ mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "",
+ mTelephonyManager.DATA_DISCONNECTED, true);
when(mConnectivityManager.getActiveNetwork()).thenReturn(null);
Drawable expectDrawable = mock(Drawable.class);
@@ -264,15 +264,10 @@
@Test
public void getMobileDrawable_hasCarrierDataAndDataIsOnCellular_getMobileDrawable()
throws Throwable {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
- Drawable drawable = mock(Drawable.class);
- int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
- mDefaultDataSubscriptionInfo);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+ true);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ Drawable drawable = mock(Drawable.class);
assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
mDrawableWithSignalStrength);
@@ -283,14 +278,9 @@
@Test
public void getMobileDrawable_hasCarrierDataAndDataIsOnWifi_getMobileDrawable()
throws Throwable {
- when(mTelephonyManager.isDataEnabled()).thenReturn(true);
- when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
- when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
- when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+ true);
Drawable drawable = mock(Drawable.class);
- int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
- when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
- mDefaultDataSubscriptionInfo);
addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
@@ -303,6 +293,16 @@
mNetworkCapabilities);
}
+ private void mockConnections(boolean isDataEnabled, int serviceState, String expectDisplayName,
+ int getDataState, boolean isWifiEnabled) {
+ when(mTelephonyManager.isDataEnabled()).thenReturn(isDataEnabled);
+ when(mWifiManager.isWifiEnabled()).thenReturn(isWifiEnabled);
+ when(mTelephonyManager.getDataState()).thenReturn(getDataState);
+
+ when(mServiceState.getState()).thenReturn(serviceState);
+ when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
+ }
+
private class TestCustomSliceable implements CustomSliceable {
TestCustomSliceable() {
}
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
index 4760daa..a323ed6 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceTest.java
@@ -236,7 +236,8 @@
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, true, true, null);
- when(mMockNetworkProviderWorker.isEthernetConnected()).thenReturn(true);
+ when(mMockNetworkProviderWorker.getInternetType())
+ .thenReturn(InternetUpdater.INTERNET_ETHERNET);
final Slice slice = mMockProviderModelSlice.getSlice();
@@ -258,7 +259,8 @@
mWifiList.add(mMockWifiSliceItem2);
mMockNetworkProviderWorker.updateSelfResults(mWifiList);
mockHelperCondition(false, true, true, mWifiList.get(0));
- when(mMockNetworkProviderWorker.isEthernetConnected()).thenReturn(true);
+ when(mMockNetworkProviderWorker.getInternetType())
+ .thenReturn(InternetUpdater.INTERNET_ETHERNET);
final Slice slice = mMockProviderModelSlice.getSlice();
diff --git a/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
similarity index 72%
rename from tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
rename to tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
index fb66d29..ee239ba 100644
--- a/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/network/VpnPreferenceControllerTest.java
@@ -22,7 +22,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -32,34 +31,38 @@
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
import android.net.VpnManager;
+import android.os.Looper;
import android.os.UserHandle;
+import android.provider.Settings;
import android.provider.SettingsSlicesContract;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.net.VpnConfig;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
+import org.junit.Ignore;
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)
+@RunWith(AndroidJUnit4.class)
public class VpnPreferenceControllerTest {
+ private static final String VPN_PREFERENCE_KEY = "vpn_settings";
- @Mock
private Context mContext;
@Mock
private ConnectivityManager mConnectivityManager;
@Mock
private VpnManager mVpnManager;
- @Mock
private PreferenceScreen mScreen;
@Mock
private Preference mPreference;
@@ -68,12 +71,21 @@
private LifecycleOwner mLifecycleOwner;
@Before
+ @UiThreadTest
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(mConnectivityManager);
when(mContext.getSystemService(VpnManager.class)).thenReturn(mVpnManager);
- when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ mScreen = preferenceManager.createPreferenceScreen(mContext);
+ when(mPreference.getKey()).thenReturn(VPN_PREFERENCE_KEY);
+ mScreen.addPreference(mPreference);
mController = spy(new VpnPreferenceController(mContext));
mLifecycleOwner = () -> mLifecycle;
@@ -83,13 +95,20 @@
@Test
public void displayPreference_available_shouldSetDependency() {
- doReturn(true).when(mController).isAvailable();
- mController.displayPreference(mScreen);
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS, "");
+ VpnPreferenceController controller = spy(new VpnPreferenceController(mContext));
+ doReturn(true).when(controller).isAvailable();
+
+ controller.displayPreference(mScreen);
verify(mPreference).setDependency(SettingsSlicesContract.KEY_AIRPLANE_MODE);
}
@Test
+ // TODO(b/176821216) re-enable this test once VpnPreferenceController is edited to notify
+ // the preference of legacy VPNs
+ @Ignore
public void goThroughLifecycle_shouldRegisterUnregisterListener() {
doReturn(true).when(mController).isAvailable();
@@ -107,7 +126,7 @@
final VpnConfig config = new VpnConfig();
config.legacy = true;
final VpnPreferenceController controller =
- new VpnPreferenceController(RuntimeEnvironment.application);
+ new VpnPreferenceController(ApplicationProvider.getApplicationContext());
final String summary = controller.getNameForVpnConfig(config, UserHandle.CURRENT);
diff --git a/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java
index 97f8e11..d052f31 100644
--- a/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java
+++ b/tests/unit/src/com/android/settings/network/telephony/NetworkProviderWorkerTest.java
@@ -261,24 +261,24 @@
}
@Test
- public void isEthernetConnected_connectedEthernet_shouldBeTrue() {
+ public void getInternetType_connectedEthernet_returnInternetEthernet() {
mMockNetworkProviderWorker.onInternetTypeChanged(INTERNET_ETHERNET);
- assertThat(mMockNetworkProviderWorker.isEthernetConnected()).isTrue();
+ assertThat(mMockNetworkProviderWorker.getInternetType()).isEqualTo(INTERNET_ETHERNET);
}
@Test
- public void isEthernetConnected_connectedWifi_shouldBeFalse() {
+ public void getInternetType_connectedWifi_returnInternetWifi() {
mMockNetworkProviderWorker.onInternetTypeChanged(INTERNET_WIFI);
- assertThat(mMockNetworkProviderWorker.isEthernetConnected()).isFalse();
+ assertThat(mMockNetworkProviderWorker.getInternetType()).isEqualTo(INTERNET_WIFI);
}
@Test
- public void isEthernetConnected_connectedCarrier_shouldBeFalse() {
+ public void getInternetType__connectedCarrier_returnInternetCellular() {
mMockNetworkProviderWorker.onInternetTypeChanged(INTERNET_CELLULAR);
- assertThat(mMockNetworkProviderWorker.isEthernetConnected()).isFalse();
+ assertThat(mMockNetworkProviderWorker.getInternetType()).isEqualTo(INTERNET_CELLULAR);
}
public class MockNetworkProviderWorker extends NetworkProviderWorker {
diff --git a/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java b/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java
new file mode 100644
index 0000000..5b0c7b5
--- /dev/null
+++ b/tests/unit/src/com/android/settings/security/SecuritySettingsFeatureProviderImplTest.java
@@ -0,0 +1,49 @@
+/*
+ * 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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SecuritySettingsFeatureProviderImplTest {
+
+ private SecuritySettingsFeatureProviderImpl mSecuritySettingsFeatureProvider;
+
+ @Before
+ public void setUp() {
+ mSecuritySettingsFeatureProvider = new SecuritySettingsFeatureProviderImpl();
+ }
+
+ @Test
+ public void hasAlternativeSecuritySettingsFragment_returnsFalse() {
+ assertThat(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment())
+ .isFalse();
+ }
+
+ @Test
+ public void getAlternativeSecuritySettingsFragmentClassname_returnsNull() {
+ String alternativeFragmentClassname =
+ mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname();
+ assertThat(alternativeFragmentClassname).isNull();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
new file mode 100644
index 0000000..a9acd2a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.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.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.preference.Preference;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class TopLevelSecurityEntryPreferenceControllerTest {
+
+ private static final String PREFERENCE_KEY = "top_level_security";
+ private static final String ALTERNATIVE_FRAGMENT_CLASSNAME = "AlternativeFragmentClassname";
+
+ private TopLevelSecurityEntryPreferenceController mTopLevelSecurityEntryPreferenceController;
+ private Preference mPreference;
+ private FakeFeatureFactory mFeatureFactory;
+ private SecuritySettingsFeatureProvider mSecuritySettingsFeatureProvider;
+
+ @Mock
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider();
+
+ mPreference = new Preference(ApplicationProvider.getApplicationContext());
+ mPreference.setKey(PREFERENCE_KEY);
+
+ doNothing().when(mContext).startActivity(any(Intent.class));
+ mTopLevelSecurityEntryPreferenceController =
+ new TopLevelSecurityEntryPreferenceController(mContext, PREFERENCE_KEY);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
+ Preference preference = new Preference(ApplicationProvider.getApplicationContext());
+ preference.setKey("some_other_preference");
+
+ boolean preferenceHandled =
+ mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(preference);
+
+ assertThat(preferenceHandled).isFalse();
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_withAlternativeFragment_launchesAlternativeFragment() {
+ when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment())
+ .thenReturn(true);
+ when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname())
+ .thenReturn(ALTERNATIVE_FRAGMENT_CLASSNAME);
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ boolean preferenceHandled =
+ mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(preferenceHandled).isTrue();
+ verify(mContext).startActivity(intentCaptor.capture());
+ assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(ALTERNATIVE_FRAGMENT_CLASSNAME);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_withDisabledAlternative_isNotHandled() {
+ when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment())
+ .thenReturn(false);
+ when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname())
+ .thenReturn(ALTERNATIVE_FRAGMENT_CLASSNAME);
+
+ boolean preferenceHandled =
+ mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(preferenceHandled).isFalse();
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_withoutAlternativeFragmentName_isNotHandled() {
+ when(mSecuritySettingsFeatureProvider.hasAlternativeSecuritySettingsFragment())
+ .thenReturn(true);
+ when(mSecuritySettingsFeatureProvider.getAlternativeSecuritySettingsFragmentClassname())
+ .thenReturn(null);
+
+ boolean preferenceHandled =
+ mTopLevelSecurityEntryPreferenceController.handlePreferenceTreeClick(mPreference);
+
+ assertThat(preferenceHandled).isFalse();
+ }
+}
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 6955dc4..b6f330b 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -28,6 +28,7 @@
import com.android.settings.dashboard.DashboardFeatureProvider;
import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -40,6 +41,7 @@
import com.android.settings.panel.PanelFeatureProvider;
import com.android.settings.search.SearchFeatureProvider;
import com.android.settings.security.SecurityFeatureProvider;
+import com.android.settings.security.SecuritySettingsFeatureProvider;
import com.android.settings.slices.SlicesFeatureProvider;
import com.android.settings.users.UserFeatureProvider;
import com.android.settings.wifi.WifiTrackerLibProvider;
@@ -53,6 +55,7 @@
public final SupportFeatureProvider supportFeatureProvider;
public final MetricsFeatureProvider metricsFeatureProvider;
public final BatteryStatusFeatureProvider batteryStatusFeatureProvider;
+ public final BatterySettingsFeatureProvider batterySettingsFeatureProvider;
public final PowerUsageFeatureProvider powerUsageFeatureProvider;
public final DashboardFeatureProvider dashboardFeatureProvider;
public final DockUpdaterFeatureProvider dockUpdaterFeatureProvider;
@@ -76,6 +79,7 @@
public WifiTrackerLibProvider wifiTrackerLibProvider;
public ExtraAppInfoFeatureProvider extraAppInfoFeatureProvider;
+ public SecuritySettingsFeatureProvider securitySettingsFeatureProvider;
/**
* Call this in {@code @Before} method of the test class to use fake factory.
@@ -92,6 +96,7 @@
supportFeatureProvider = mock(SupportFeatureProvider.class);
metricsFeatureProvider = mock(MetricsFeatureProvider.class);
batteryStatusFeatureProvider = mock(BatteryStatusFeatureProvider.class);
+ batterySettingsFeatureProvider = mock(BatterySettingsFeatureProvider.class);
powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
dockUpdaterFeatureProvider = mock(DockUpdaterFeatureProvider.class);
@@ -113,6 +118,7 @@
mFaceFeatureProvider = mock(FaceFeatureProvider.class);
wifiTrackerLibProvider = mock(WifiTrackerLibProvider.class);
extraAppInfoFeatureProvider = mock(ExtraAppInfoFeatureProvider.class);
+ securitySettingsFeatureProvider = mock(SecuritySettingsFeatureProvider.class);
}
@Override
@@ -136,6 +142,11 @@
}
@Override
+ public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+ return batterySettingsFeatureProvider;
+ }
+
+ @Override
public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
return powerUsageFeatureProvider;
}
@@ -234,4 +245,9 @@
public ExtraAppInfoFeatureProvider getExtraAppInfoFeatureProvider() {
return extraAppInfoFeatureProvider;
}
+
+ @Override
+ public SecuritySettingsFeatureProvider getSecuritySettingsFeatureProvider() {
+ return securitySettingsFeatureProvider;
+ }
}
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java
index a2b99bf..0729780 100644
--- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherMaximizeCompatibilityPreferenceControllerTest.java
@@ -121,20 +121,20 @@
}
@Test
- public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnTure() {
+ public void isMaximizeCompatibilityEnabled_concurrencySupportedAndEnabled_returnFalse() {
// The preconditions are ready in setup().
- assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true);
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false);
}
@Test
- public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnFalse() {
+ public void isMaximizeCompatibilityEnabled_concurrencySupportedAndDisabled_returnTrue() {
SoftApConfiguration config = new SoftApConfiguration.Builder()
.setBridgedModeOpportunisticShutdownEnabled(false)
.build();
doReturn(config).when(mWifiManager).getSoftApConfiguration();
- assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(false);
+ assertThat(mController.isMaximizeCompatibilityEnabled()).isEqualTo(true);
}
@Test
@@ -171,25 +171,25 @@
}
@Test
- public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setDisabled() {
+ public void setupMaximizeCompatibility_concurrencySupportedAndDisabled_setEnabled() {
// 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);
+ assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(true);
}
@Test
- public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setEnabled() {
+ public void setupMaximizeCompatibility_concurrencySupportedAndEnabled_setDisabled() {
// 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);
+ assertThat(builder.build().isBridgedModeOpportunisticShutdownEnabled()).isEqualTo(false);
}
@Test