Merge "Remove unnecessary async" into tm-dev
diff --git a/Android.bp b/Android.bp
index fd66974..0940b0b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,6 +75,7 @@
"lottie",
"WifiTrackerLib",
"windowExtLib",
+ "SettingsLibActivityEmbedding",
],
libs: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 290f84e..0279eec 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4349,6 +4349,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index 16658f8..ba4d2f6 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -3736,7 +3736,7 @@
errorLine1=" android:color="@color/accessibility_feature_background"/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
- file="res/drawable/ic_text_and_display.xml"
+ file="res/drawable/ic_color_and_motion.xml"
line="22"
column="13"/>
</issue>
diff --git a/res/color/dream_card_color_state_list.xml b/res/color/dream_card_color_state_list.xml
index b5de7b4..b0c86bb 100644
--- a/res/color/dream_card_color_state_list.xml
+++ b/res/color/dream_card_color_state_list.xml
@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
- <item android:state_selected="true" android:color="?androidprv:attr/colorSurfaceVariant"/>
- <item android:color="?androidprv:attr/colorSurfaceHighlight"/>
+ <item android:state_selected="true" android:color="?androidprv:attr/colorAccentPrimary"/>
+ <item android:color="?androidprv:attr/colorSurface"/>
</selector>
\ No newline at end of file
diff --git a/res/color/dream_card_text_color_state_list.xml b/res/color/dream_card_text_color_state_list.xml
new file mode 100644
index 0000000..438855f
--- /dev/null
+++ b/res/color/dream_card_text_color_state_list.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:state_selected="true" android:color="?androidprv:attr/textColorOnAccent"/>
+ <item android:color="?android:attr/textColorPrimary"/>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml b/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml
new file mode 100644
index 0000000..2583b6f
--- /dev/null
+++ b/res/drawable/accessibility_auto_added_qs_tooltips_illustration.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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="178dp"
+ android:height="150dp"
+ android:viewportWidth="178"
+ android:viewportHeight="150">
+ <path
+ android:pathData="M161.612,147.208L16.619,147.208A13,13 0,0 1,3.619 134.208L3.619,-442.725A13,13 0,0 1,16.619 -455.725L161.612,-455.725A13,13 0,0 1,174.612 -442.725L174.612,134.208A13,13 0,0 1,161.612 147.208z"
+ android:strokeWidth="6"
+ android:fillColor="#ffffff"
+ android:strokeColor="#EDEDED"/>
+ <path
+ android:pathData="M28.47,103.945L48.47,103.945A12,12 0,0 1,60.47 115.945L60.47,115.945A12,12 0,0 1,48.47 127.945L28.47,127.945A12,12 0,0 1,16.47 115.945L16.47,115.945A12,12 0,0 1,28.47 103.945z"
+ android:fillColor="#797272"/>
+ <path
+ android:pathData="M38.5,115.5m-15.5,0a15.5,15.5 0,1 1,31 0a15.5,15.5 0,1 1,-31 0"
+ android:fillColor="#BCEDDF"
+ android:fillAlpha="0.5"/>
+ <path
+ android:pathData="M45.279,108.52L46.48,109.72C47.182,110.414 47.182,111.543 46.48,112.237L34.717,124H31V120.283L42.763,108.52C43.457,107.827 44.586,107.827 45.279,108.52ZM32.778,122.222L34.032,122.275L42.763,113.535L41.51,112.281L32.778,121.013V122.222Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M79,104L99,104A12,12 0,0 1,111 116L111,116A12,12 0,0 1,99 128L79,128A12,12 0,0 1,67 116L67,116A12,12 0,0 1,79 104z"
+ android:fillColor="#E0DCDC"/>
+ <path
+ android:pathData="M79,104L99,104A12,12 0,0 1,111 116L111,116A12,12 0,0 1,99 128L79,128A12,12 0,0 1,67 116L67,116A12,12 0,0 1,79 104z"
+ android:fillColor="#E7E7E7"/>
+ <path
+ android:pathData="M129,104L149,104A12,12 0,0 1,161 116L161,116A12,12 0,0 1,149 128L129,128A12,12 0,0 1,117 116L117,116A12,12 0,0 1,129 104z"
+ android:fillColor="#E0DCDC"/>
+ <path
+ android:pathData="M129,104L149,104A12,12 0,0 1,161 116L161,116A12,12 0,0 1,149 128L129,128A12,12 0,0 1,117 116L117,116A12,12 0,0 1,129 104z"
+ android:fillColor="#E7E7E7"/>
+ <path
+ android:pathData="M22,14L76.49,14A6,6 0,0 1,82.49 20L82.49,38.782A6,6 0,0 1,76.49 44.782L22,44.782A6,6 0,0 1,16 38.782L16,20A6,6 0,0 1,22 14z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M22,56L76.49,56A6,6 0,0 1,82.49 62L82.49,80.782A6,6 0,0 1,76.49 86.782L22,86.782A6,6 0,0 1,16 80.782L16,62A6,6 0,0 1,22 56z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M101,14L155.49,14A6,6 0,0 1,161.49 20L161.49,38.782A6,6 0,0 1,155.49 44.782L101,44.782A6,6 0,0 1,95 38.782L95,20A6,6 0,0 1,101 14z"
+ android:fillColor="#EDEDED"/>
+ <path
+ android:pathData="M101,56L155.49,56A6,6 0,0 1,161.49 62L161.49,80.782A6,6 0,0 1,155.49 86.782L101,86.782A6,6 0,0 1,95 80.782L95,62A6,6 0,0 1,101 56z"
+ android:fillColor="#EDEDED"/>
+</vector>
diff --git a/res/drawable/dream_default_preview_icon.xml b/res/drawable/dream_default_preview_icon.xml
new file mode 100644
index 0000000..7d247bb
--- /dev/null
+++ b/res/drawable/dream_default_preview_icon.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2022 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:fillColor="?android:attr/textColorSecondary"
+ android:pathData="M3,19Q2.175,19 1.588,18.413Q1,17.825 1,17V7Q1,6.175 1.588,5.588Q2.175,5 3,5H13Q13.825,5 14.413,5.588Q15,6.175 15,7V17Q15,17.825 14.413,18.413Q13.825,19 13,19ZM3,17H13Q13,17 13,17Q13,17 13,17V7Q13,7 13,7Q13,7 13,7H3Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17ZM17,19V5H19V19ZM21,19V5H23V19ZM4,15H12L9.4,11.5L7.5,14L6.1,12.15ZM3,7Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17Q3,17 3,17Q3,17 3,17V7Q3,7 3,7Q3,7 3,7Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/dream_preview_rounded_bg.xml b/res/drawable/dream_preview_rounded_bg.xml
new file mode 100644
index 0000000..4da0c7c1
--- /dev/null
+++ b/res/drawable/dream_preview_rounded_bg.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/colorSurfaceHighlight" />
+ <corners android:radius="@dimen/dream_item_corner_radius"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_text_and_display.xml b/res/drawable/ic_color_and_motion.xml
similarity index 100%
rename from res/drawable/ic_text_and_display.xml
rename to res/drawable/ic_color_and_motion.xml
diff --git a/res/layout/accessibility_qs_tooltips.xml b/res/layout/accessibility_qs_tooltips.xml
index 85d9c52..2bc9f5f 100644
--- a/res/layout/accessibility_qs_tooltips.xml
+++ b/res/layout/accessibility_qs_tooltips.xml
@@ -23,10 +23,10 @@
android:background="@drawable/accessibility_qs_tooltips_background">
<ImageView
+ android:id="@+id/qs_illustration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/accessibility_qs_tooltips_margin_top"
- android:src="@drawable/accessibility_qs_tooltips_illustration"
android:layout_gravity="center_horizontal"
android:contentDescription="@null" />
diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml
index 1de5705..fcdcb5e 100644
--- a/res/layout/apps_filter_spinner.xml
+++ b/res/layout/apps_filter_spinner.xml
@@ -21,13 +21,11 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/filter_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_centerHorizontal="true"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
android:theme="@style/Widget.PopupWindow.Settings"/>
<ImageView
diff --git a/res/layout/data_usage_cycles.xml b/res/layout/data_usage_cycles.xml
index 05c38e0..c957f9c 100644
--- a/res/layout/data_usage_cycles.xml
+++ b/res/layout/data_usage_cycles.xml
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/cycles_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
diff --git a/res/layout/dream_picker_layout.xml b/res/layout/dream_picker_layout.xml
index 6de7ff6..68e2051 100644
--- a/res/layout/dream_picker_layout.xml
+++ b/res/layout/dream_picker_layout.xml
@@ -15,32 +15,25 @@
limitations under the License.
-->
-<LinearLayout
+
+<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <androidx.cardview.widget.CardView
- android:id="@+id/dream_picker_container"
- android:layout_width="match_parent"
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/dream_list"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- style="@style/DreamPickerBackgroundStyle">
+ android:clipToPadding="true"
+ android:nestedScrollingEnabled="false"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="@dimen/dream_preference_card_padding"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"/>
- <androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/dream_list"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:clipToPadding="true"
- android:nestedScrollingEnabled="false"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"/>
-
- </androidx.constraintlayout.widget.ConstraintLayout>
-
- </androidx.cardview.widget.CardView>
-</LinearLayout>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/res/layout/dream_preference_layout.xml b/res/layout/dream_preference_layout.xml
index 2bc67ba..abb2a34 100644
--- a/res/layout/dream_preference_layout.xml
+++ b/res/layout/dream_preference_layout.xml
@@ -17,7 +17,6 @@
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
@@ -30,21 +29,28 @@
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight">
- <androidx.cardview.widget.CardView
- android:id="@+id/preview_container"
- android:layout_width="match_parent"
+ <ImageView
+ android:id="@+id/preview"
+ android:layout_width="0dp"
android:layout_height="0dp"
- app:cardCornerRadius="@dimen/dream_item_corner_radius"
- app:cardElevation="0dp"
+ android:background="@drawable/dream_preview_rounded_bg"
+ android:importantForAccessibility="no"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent">
- <ImageView
- android:id="@+id/preview"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerCrop"/>
- </androidx.cardview.widget.CardView>
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <ImageView
+ android:id="@+id/preview_placeholder"
+ android:layout_width="@dimen/dream_preview_placeholder_width"
+ android:layout_height="0dp"
+ android:src="@drawable/dream_default_preview_icon"
+ android:importantForAccessibility="no"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="@+id/preview"
+ app:layout_constraintBottom_toBottomOf="@+id/preview"
+ app:layout_constraintStart_toStartOf="@+id/preview"
+ app:layout_constraintEnd_toEndOf="@+id/preview"/>
<Button
android:id="@+id/customize_button"
@@ -53,10 +59,10 @@
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/customize_button_title"
- app:layout_constraintTop_toTopOf="@+id/preview_container"
- app:layout_constraintBottom_toBottomOf="@+id/preview_container"
- app:layout_constraintStart_toStartOf="@+id/preview_container"
- app:layout_constraintEnd_toEndOf="@+id/preview_container"/>
+ app:layout_constraintTop_toTopOf="@+id/preview"
+ app:layout_constraintBottom_toBottomOf="@+id/preview"
+ app:layout_constraintStart_toStartOf="@+id/preview"
+ app:layout_constraintEnd_toEndOf="@+id/preview"/>
<ImageView
android:id="@+id/icon"
@@ -66,9 +72,10 @@
android:layout_marginStart="@dimen/dream_item_icon_margin_start"
android:layout_marginBottom="@dimen/dream_item_title_margin_bottom"
android:gravity="center_vertical"
+ android:importantForAccessibility="no"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintTop_toBottomOf="@+id/preview_container"
+ app:layout_constraintTop_toBottomOf="@+id/preview"
app_layout_constraintEnd_toStartOf="@+id/title_text"
app:layout_constraintStart_toStartOf="parent"/>
@@ -82,8 +89,9 @@
android:gravity="center_vertical"
android:maxLines="1"
android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceListItemSmall"
- app:layout_constraintTop_toBottomOf="@+id/preview_container"
+ android:textSize="16sp"
+ android:textColor="@color/dream_card_text_color_state_list"
+ app:layout_constraintTop_toBottomOf="@+id/preview"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/icon"/>
diff --git a/res/layout/lockscreen_remote_input.xml b/res/layout/lockscreen_remote_input.xml
deleted file mode 100644
index 4fa44ce..0000000
--- a/res/layout/lockscreen_remote_input.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2016 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <CheckBox
- android:id="@+id/lockscreen_remote_input"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_marginStart="20dp"
- android:paddingStart="20dp"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorAlertDialogListItem"
- android:gravity="center_vertical"
- android:text="@string/lockscreen_remote_input"
- />
-
- <ImageView
- android:id="@+id/restricted_lock_icon_remote_input"
- android:layout_width="@*android:dimen/config_restrictedIconSize"
- android:layout_height="@*android:dimen/config_restrictedIconSize"
- android:tint="?android:attr/colorAccent"
- android:src="@*android:drawable/ic_info"
- android:layout_marginEnd="?android:attr/dialogPreferredPadding"
- android:layout_gravity="center_vertical"
- android:scaleType="centerInside" />
-</LinearLayout>
diff --git a/res/layout/manage_apps_filter_spinner.xml b/res/layout/manage_apps_filter_spinner.xml
index 8283bb8..a2d0e12 100644
--- a/res/layout/manage_apps_filter_spinner.xml
+++ b/res/layout/manage_apps_filter_spinner.xml
@@ -21,12 +21,10 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/filter_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="24dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
android:theme="@style/Widget.PopupWindow.Settings"/>
</FrameLayout>
diff --git a/res/layout/restricted_dialog_singlechoice.xml b/res/layout/restricted_dialog_singlechoice.xml
index a9984a8..9b19884 100644
--- a/res/layout/restricted_dialog_singlechoice.xml
+++ b/res/layout/restricted_dialog_singlechoice.xml
@@ -35,14 +35,4 @@
android:drawableStart="?android:attr/listChoiceIndicatorSingle"
android:drawablePadding="20dp"
android:ellipsize="marquee" />
- <ImageView
- android:id="@+id/restricted_lock_icon"
- android:layout_width="@*android:dimen/config_restrictedIconSize"
- android:layout_height="@*android:dimen/config_restrictedIconSize"
- android:tint="?android:attr/colorAccent"
- android:src="@*android:drawable/ic_info"
- android:layout_marginLeft="@dimen/restricted_icon_padding"
- android:baselineAlignBottom="true"
- android:scaleType="centerInside"
- android:visibility="gone" />
</com.android.settings.CheckableLinearLayout>
diff --git a/res/layout/restricted_preference_dropdown.xml b/res/layout/restricted_preference_dropdown.xml
deleted file mode 100644
index 8930e24..0000000
--- a/res/layout/restricted_preference_dropdown.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2016 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <view android:id="@+id/spinner"
- class="com.android.settings.notification.RestrictedDropDownPreference$ReselectionSpinner"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:visibility="invisible"
- android:layout_marginStart="@dimen/preference_no_icon_padding_start"/>
-
- <include layout="@layout/settingslib_preference" />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/restricted_radio_with_summary.xml b/res/layout/restricted_radio_with_summary.xml
deleted file mode 100644
index 5e7fcd8..0000000
--- a/res/layout/restricted_radio_with_summary.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.settings.CheckableLinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="?android:attr/selectableItemBackground"
- android:minHeight="?android:attr/listPreferredItemHeightSmall">
-
- <com.android.settings.CheckableLinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingStart="20dp"
- android:paddingEnd="?android:attr/dialogPreferredPadding">
- <CheckedTextView
- android:id="@android:id/title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorAlertDialogListItem"
- android:gravity="center_vertical"
- android:drawableStart="?android:attr/listChoiceIndicatorSingle"
- android:ellipsize="marquee" />
- <ImageView
- android:id="@+id/restricted_icon"
- android:layout_width="@*android:dimen/config_restrictedIconSize"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:tint="?android:attr/colorAccent"
- android:src="@*android:drawable/ic_info"
- android:layout_marginLeft="@dimen/restricted_icon_padding"
- android:visibility="gone" />
- </com.android.settings.CheckableLinearLayout>
-
-
- <TextView android:id="@android:id/summary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="8dp"
- android:paddingStart="52dp"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="10" />
-
-</com.android.settings.CheckableLinearLayout>
diff --git a/res/layout/settings_homepage_container.xml b/res/layout/settings_homepage_container.xml
index 4f0d804..f0aafe1 100644
--- a/res/layout/settings_homepage_container.xml
+++ b/res/layout/settings_homepage_container.xml
@@ -19,7 +19,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/settings_homepage_container"
- android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout/spinner_dropdown_restricted_item.xml b/res/layout/spinner_dropdown_restricted_item.xml
deleted file mode 100644
index d95e4be..0000000
--- a/res/layout/spinner_dropdown_restricted_item.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical">
- <TextView android:id="@android:id/text1"
- style="?android:attr/spinnerDropDownItemStyle"
- android:singleLine="true"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="?android:attr/listPreferredItemHeightSmall"
- android:ellipsize="marquee" />
- <ImageView android:id="@+id/restricted_icon"
- android:layout_width="@*android:dimen/config_restrictedIconSize"
- android:layout_height="@*android:dimen/config_restrictedIconSize"
- android:tint="?android:attr/colorAccent"
- android:src="@*android:drawable/ic_info"
- android:baselineAlignBottom="true"
- android:layout_marginEnd="@dimen/restricted_icon_padding"
- android:gravity="end|center_vertical"
- android:visibility="gone" />
-</LinearLayout>
diff --git a/res/layout/tare_dropdown_page.xml b/res/layout/tare_dropdown_page.xml
index 79931e8..674b189 100644
--- a/res/layout/tare_dropdown_page.xml
+++ b/res/layout/tare_dropdown_page.xml
@@ -6,15 +6,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".development.tare.DropdownActivity">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/spinner"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- android:theme="@style/Widget.PopupWindow.Settings"
- android:padding="10dp" />
+ android:theme="@style/Widget.PopupWindow.Settings" />
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="match_parent"
diff --git a/res/layout/tare_homepage.xml b/res/layout/tare_homepage.xml
index 2c184d2..ddc9333 100644
--- a/res/layout/tare_homepage.xml
+++ b/res/layout/tare_homepage.xml
@@ -60,5 +60,6 @@
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:onClick="revertSettings"
- android:text="@string/tare_revert" />
+ android:text="@string/tare_revert"
+ style="@style/ActionPrimaryButton" />
</LinearLayout>
diff --git a/res/values/config.xml b/res/values/config.xml
index 6a7c15b..bf78fd7 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -447,6 +447,9 @@
<!-- Whether top_level_battery should be shown or not. -->
<bool name="config_show_top_level_battery">true</bool>
+ <!-- Whether to use compact battery status string. -->
+ <bool name="config_use_compact_battery_status">false</bool>
+
<!-- Whether top_level_connected_devices should be shown or not. -->
<bool name="config_show_top_level_connected_devices">true</bool>
@@ -567,7 +570,19 @@
<item>2</item>
<item>3</item>
</integer-array>
-
+
+ <!-- The settings/preference description for each settable device state defined in the array
+ "config_perDeviceStateRotationLockDefaults".
+ The item in position "i" describes the auto-rotation setting for the device state also in
+ position "i" in the array "config_perDeviceStateRotationLockDefaults". -->
+ <string-array name="config_settableAutoRotationDeviceStatesDescriptions">
+ <!-- Example:
+ <item>Auto-rotate when folded</item>
+ <item>@null</item> No description for state in position 1
+ <item>Auto-rotate when unfolded</item>
+ -->
+ </string-array>
+
<!-- Whether to aggregate for network selection list-->
<bool name="config_network_selection_list_aggregation_enabled">false</bool>
@@ -593,4 +608,5 @@
<!-- Whether the dream setup activity should be enabled as part of setupwizard -->
<bool name="dream_setup_supported">false</bool>
+
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index be1ba5b..78dbcce 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -441,7 +441,7 @@
<dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
<!-- Dimensions for Dream settings cards -->
- <dimen name="dream_item_min_column_width">210dp</dimen>
+ <dimen name="dream_item_min_column_width">174dp</dimen>
<dimen name="dream_item_corner_radius">28dp</dimen>
<dimen name="dream_item_content_padding">8dp</dimen>
<dimen name="dream_item_icon_size">20dp</dimen>
@@ -449,9 +449,10 @@
<dimen name="dream_item_title_margin_bottom">8dp</dimen>
<dimen name="dream_item_title_margin_start">18dp</dimen>
<dimen name="dream_item_icon_margin_start">10dp</dimen>
- <dimen name="dream_preference_margin_horizontal">10dp</dimen>
+ <dimen name="dream_preference_card_padding">16dp</dimen>
<dimen name="dream_preference_margin_bottom">20dp</dimen>
<dimen name="dream_picker_margin_horizontal">48dp</dimen>
+ <dimen name="dream_preview_placeholder_width">52dp</dimen>
<!-- Sims/Data mobile/Calls/SMS select dialog-->
<dimen name="sims_select_margin_bottom">24dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5472a9d..7726b40 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3186,7 +3186,7 @@
<!-- [CHAR LIMIT=30] Sound & display settings screen, setting option name to change font size -->
<string name="title_font_size">Font size</string>
<!-- Summary for Font size. Lets the user know that this will make text larger or smaller. Appears in the accessibility portion of setup wizard. [CHAR LIMIT=NONE] -->
- <string name="short_summary_font_size">Make text larger or smaller</string>
+ <string name="short_summary_font_size">Make text bigger or smaller</string>
<!-- Sound & display settings screen, setting option summary displaying the currently selected font size -->
<string name="summary_font_size" translatable="false">%1$s</string>
@@ -5156,8 +5156,8 @@
<string name="general_category_title">General</string>
<!-- Title for the accessibility preference category of display services and settings. [CHAR LIMIT=50] -->
<string name="display_category_title">Display</string>
- <!-- Title for the accessibility text options page. [CHAR LIMIT=50] -->
- <string name="accessibility_text_and_display_title">Text and display</string>
+ <!-- Title for the accessibility color and motion page. [CHAR LIMIT=50] -->
+ <string name="accessibility_color_and_motion_title">Color and motion</string>
<!-- Title for the accessibility text options page. [CHAR LIMIT=50] -->
<string name="accessibility_turn_screen_darker_title">Turn screen darker</string>
<!-- Title for the accessibility preference category of interaction control services and settings. [CHAR LIMIT=50] -->
@@ -5523,8 +5523,10 @@
<string name="accessibility_service_primary_switch_title">Use <xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g></string>
<!-- Used in the accessibility service settings to open the activity. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_primary_open_title">Open <xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g></string>
+ <!-- Used in the accessibility service settings to show quick settings tooltips for auto-added feature. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_service_auto_added_qs_tooltips_content"><xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g> added to Quick Settings. Swipe down to turn it on or off anytime.</string>
<!-- Used in the accessibility service settings to show quick settings tooltips. [CHAR LIMIT=NONE] -->
- <string name="accessibility_service_quick_settings_tooltips_content"><xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g> added to Quick Settings. Swipe down to turn it on or off anytime.</string>
+ <string name="accessibility_service_qs_tooltips_content">You can add a shortcut to <xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g> by editing quick settings</string>
<!-- Used in the accessibility action for accessibility quick settings tooltips to dismiss. [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_tooltips_dismiss">Dismiss</string>
<!-- Intro for color correction settings screen to control turning on/off the feature entirely. [CHAR LIMIT=NONE] -->
@@ -7675,8 +7677,8 @@
<string name="nfc_default_payment_workapp_confirmation_message_title">To make a payment using a work app:</string>
<!-- Text string in the dialog [CHAR LIMIT=60] -->
<string name="nfc_default_payment_workapp_confirmation_message_1">work profile must be turned on.</string>
- <!-- Text string in the dialog [CHAR LIMIT=60] -->
- <string name="nfc_default_payment_workapp_confirmation_message_2">you\u2019ll need to enter your work profile lock if you have one.</string>
+ <!-- Text string in the dialog [CHAR LIMIT=80] -->
+ <string name="nfc_default_payment_workapp_confirmation_message_2">you\u2019ll need to enter your work PIN, pattern, or password if you have one.</string>
<!-- Caption for button linking to a page explaining how Tap and Pay works-->
<string name="nfc_payment_how_it_works">How it works</string>
@@ -10948,7 +10950,7 @@
<!-- Title of setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=40] -->
<string name="screen_zoom_title">Display size</string>
<!-- Short summary for Magnification gesture. Lets the user know that this makes items on screen larger or smaller. Appears in the accessibility portion of Setup Wizard. [CHAR LIMIT=NONE] -->
- <string name="screen_zoom_short_summary">Make items on screen larger or smaller</string>
+ <string name="screen_zoom_short_summary">Make everything bigger or smaller</string>
<!-- Keywords for setting that controls screen zoom (e.g. how large interface elements appear). [CHAR LIMIT=NONE] -->
<string name="screen_zoom_keywords">display density, screen zoom, scale, scaling</string>
<!-- Summary of screen zoom setting screen. [CHAR LIMIT=NONE] -->
@@ -13330,7 +13332,7 @@
<!-- Wallet (formerly Cards and passes) toggle name [CHAR LIMIT=60] -->
<string name="lockscreen_privacy_wallet_setting_toggle">Show wallet</string>
<!-- Wallet (formerly Cards and passes) summary [CHAR LIMIT=NONE] -->
- <string name="lockscreen_privacy_wallet_summary">Allow access to wallet from lock screen and quick settings</string>
+ <string name="lockscreen_privacy_wallet_summary">Allow access to wallet from lock screen</string>
<!-- QR Code Scanner toggle name [CHAR LIMIT=60] -->
<string name="lockscreen_privacy_qr_code_scanner_setting_toggle">Show QR scanner</string>
@@ -13729,18 +13731,26 @@
translate "maximum balance when device is fully charged" instead. Balance is the same meaning as
having money in a bank account. Balance in our feature is the amount of Android Resource Credits
an app can have. Android Resource Credits are a form of payment used by apps to be able to
- perform tasks. [CHAR LIMIT=40]-->
+ perform tasks. [CHAR LIMIT=80]-->
<string name="tare_max_satiated_balance">Maximum Satiated Balance</string>
- <!-- Title for the TARE policy factor that determines the maximum credits in circulation between
- all the apps [CHAR LIMIT=40]-->
- <string name="tare_max_circulation">Maximum Circulation</string>
- <!-- Title for the TARE policy factor that determines the minimum credits an app can have in one
- battery life cycle. Satiated means battery is fully charged; If this is not easily translatable,
- translate "minimum balance when device is fully charged" instead. Balance is the same meaning as
- having money in a bank account. Balance in our feature is the amount of Android Resource Credits
- an app can have. Android Resource Credits are a form of payment used by apps to be able to
- perform tasks. [CHAR LIMIT=40]-->
- <string name="tare_min_satiated_balance">Minimum Satiated Balance</string>
+ <!-- Title for the TARE policy factors that affect how many credits an app may have. Balance
+ in this context is the same as "bank balance" or "account balance" (ie. how much "money" may be
+ in a bank account). [CHAR LIMIT=55]-->
+ <string name="tare_balances">Balances</string>
+ <!-- Title for the TARE section to modify consumption limits. "Consumption" refers to the idea
+ using resources that are not replenished. [CHAR LIMIT=55]-->
+ <string name="tare_consumption_limits">Consumption Limits</string>
+ <!-- Title for the TARE policy factor that determines the initial maximum amount of credits that
+ can be consumed by all the apps [CHAR LIMIT=80]-->
+ <string name="tare_initial_consumption_limit">Initial Consumption Limit</string>
+ <!-- Title for the TARE policy factor that determines the maximum consumption limit the system
+ can have [CHAR LIMIT=80]-->
+ <string name="tare_hard_consumption_limit">Maximum Consumption Limit</string>
+ <!-- Titles for the consumption limits factors. [CHAR LIMIT=40]-->
+ <string-array name="tare_consumption_limit_subfactors" translatable="false">
+ <item>@string/tare_initial_consumption_limit</item>
+ <item>@string/tare_hard_consumption_limit</item>
+ </string-array>
<!-- Title for the various modifiers that alter the cost of TARE tasks based on battery status
(charging, power save mode, etc.) [CHAR LIMIT=40]-->
<string name="tare_modifiers">Modifiers</string>
@@ -13776,14 +13786,6 @@
<string name="tare_nonwakeup_inexact" translatable="false">Inexact NonWakeup Alarm</string>
<!-- Title for the AlarmClock alarm set via AlarmManager.setAlarmClock() [CHAR LIMIT=50]-->
<string name="tare_alarm_clock" translatable="false">AlarmClock</string>
- <!-- Exempted apps are those apps exempted from most power saving features. [CHAR LIMIT=50]-->
- <string name="tare_exempted">Exempted</string>
- <!-- A headless system app is a preinstalled app that does not have any activities/UI that the
- user can interact with. [CHAR LIMIT=50]-->
- <string name="tare_headless_app">Headless System App</string>
- <!-- Other apps are those apps interacted with by users that are not exempted or headless
- system apps. [CHAR LIMIT=50]-->
- <string name="tare_other_app">Other App</string>
<!-- Top activity means an app is in the TOP android process state and is thus visible to the
user[CHAR LIMIT=50]-->
<string name="tare_top_activity">Top Activity</string>
@@ -13840,12 +13842,29 @@
<!-- Title for the penalty an app receives for letting a job use the maximum execution time and
time out [CHAR LIMIT=50]-->
<string name="tare_job_timeout_penalty">Job Timeout Penalty</string>
+ <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+ device is fully charged. "Satiated" in this context means that the battery is fully charged.
+ Balance is the same meaning as having money in a bank account.
+ Exempted apps are those apps exempted from most power saving features. [CHAR LIMIT=80]-->
+ <string name="tare_min_balance_exempted">Minimum Satiated Balance (Exempted)</string>
+ <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+ device is fully charged. "Satiated" in this context means that the battery is fully charged.
+ Balance is the same meaning as having money in a bank account.
+ A headless system app is a preinstalled app that does not have any activities/UI that the
+ user can interact with. [CHAR LIMIT=80]-->
+ <string name="tare_min_balance_headless_app">Minimum Satiated Balance (Headless System App)</string>
+ <!-- Title for the TARE factor that determines the minimum credits an app should have when the
+ device is fully charged. "Satiated" in this context means that the battery is fully charged.
+ Balance is the same meaning as having money in a bank account.
+ Remaining apps are those apps that don't fit into predefined categories. [CHAR LIMIT=80]-->
+ <string name="tare_min_balance_other_app">Minimum Satiated Balance (Remaining Apps)</string>
<!-- Titles for the minimum satiated credit balances for different types of apps
(per battery cycle). Satiated means battery is fully charged. [CHAR LIMIT=40]-->
- <string-array name="tare_min_satiated_balance_subfactors" translatable="false">
- <item>@string/tare_exempted</item>
- <item>@string/tare_headless_app</item>
- <item>@string/tare_other_app</item>
+ <string-array name="tare_app_balance_subfactors" translatable="false">
+ <item>@string/tare_max_satiated_balance</item>
+ <item>@string/tare_min_balance_exempted</item>
+ <item>@string/tare_min_balance_headless_app</item>
+ <item>@string/tare_min_balance_other_app</item>
</string-array>
<!-- Various modifier subfactors that alter the cost of TARE tasks depending on what battery
state the device is in [CHAR LIMIT=50]-->
@@ -13905,11 +13924,13 @@
<!-- TARE dialog button to proceed with a value change [CHAR LIMIT=none] -->
<string name="tare_dialog_confirm_button_title">Confirm</string>
<!-- Button to preview the selected screensaver in settings [CHAR LIMIT=40] -->
- <string name="dream_preview_button_title">Preview screensaver</string>
+ <string name="dream_preview_button_title">Preview</string>
<!-- The title of the category to show for the screensaver selector in settings [CHAR LIMIT=none] -->
- <string name="dream_picker_category">Choose an image</string>
- <!-- The title of the category to show for the screensaver overlay selector in settings [CHAR LIMIT=none] -->
- <string name="dream_complications_picker_category">Choose more options</string>
+ <string name="dream_picker_category">Choose a screen saver</string>
+ <!-- The title of the toggle which enables/disables overlays on top of the screen saver [CHAR LIMIT=none] -->
+ <string name="dream_complications_toggle_title">Show additional information</string>
+ <!-- The summary of what overlays this toggle controls [CHAR LIMIT=none] -->
+ <string name="dream_complications_toggle_summary">Display time, date and weather on the screen saver</string>
<!-- The title of the category to show for the screensaver miscellaneous settings [CHAR LIMIT=none] -->
<string name="dream_more_settings_category">More settings</string>
<!-- The title of the screen saver setup page [CHAR LIMIT=none] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a64eb1f..ebaea6b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -944,23 +944,10 @@
</style>
<style name="DreamCardStyle">
- <item name="android:layout_marginBottom">@dimen/dream_preference_margin_bottom</item>
- <item name="android:layout_marginStart">@dimen/dream_preference_margin_horizontal</item>
- <item name="android:layout_marginEnd">@dimen/dream_preference_margin_horizontal</item>
<item name="cardBackgroundColor">@color/dream_card_color_state_list</item>
<item name="cardCornerRadius">@dimen/dream_item_corner_radius</item>
<item name="cardElevation">0dp</item>
<item name="rippleColor">?android:attr/colorControlHighlight</item>
<item name="contentPadding">@dimen/dream_item_content_padding</item>
</style>
-
- <style name="DreamPickerBackgroundStyle">
- <item name="android:layout_marginBottom">48dp</item>
- <item name="android:layout_marginStart">@dimen/dream_picker_margin_horizontal</item>
- <item name="android:layout_marginEnd">@dimen/dream_picker_margin_horizontal</item>
- <item name="cardBackgroundColor">?androidprv:attr/colorSurface</item>
- <item name="cardCornerRadius">24dp</item>
- <item name="cardElevation">0dp</item>
- <item name="contentPadding">32dp</item>
- </style>
</resources>
diff --git a/res/xml/accessibility_text_and_display.xml b/res/xml/accessibility_color_and_motion.xml
similarity index 62%
rename from res/xml/accessibility_text_and_display.xml
rename to res/xml/accessibility_color_and_motion.xml
index 659acab..f251d1e 100644
--- a/res/xml/accessibility_text_and_display.xml
+++ b/res/xml/accessibility_color_and_motion.xml
@@ -17,34 +17,9 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:key="accessibility_text_and_display"
+ android:key="accessibility_color_and_motion"
android:persistent="false"
- android:title="@string/accessibility_text_and_display_title">
-
- <Preference
- android:fragment="com.android.settings.display.ToggleFontSizePreferenceFragment"
- android:icon="@drawable/ic_font_size"
- android:key="font_size_preference_screen"
- android:persistent="false"
- android:title="@string/title_font_size"
- settings:controller="com.android.settings.display.FontSizePreferenceController"
- settings:searchable="false"/>
-
- <com.android.settings.display.ScreenZoomPreference
- android:fragment="com.android.settings.display.ScreenZoomSettings"
- android:icon="@drawable/ic_screen_zoom"
- android:key="accessibility_settings_screen_zoom"
- android:persistent="false"
- android:title="@string/screen_zoom_title"
- settings:searchable="false"/>
-
- <SwitchPreference
- android:key="toggle_force_bold_text"
- android:icon="@drawable/ic_force_bold"
- android:persistent="false"
- android:title="@string/force_bold_text"
- settings:keywords="@string/keywords_bold_text"
- settings:controller="com.android.settings.accessibility.FontWeightAdjustmentPreferenceController"/>
+ android:title="@string/accessibility_color_and_motion_title">
<Preference
android:fragment="com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment"
@@ -76,12 +51,6 @@
android:title="@string/accessibility_toggle_large_pointer_icon_title"
settings:controller="com.android.settings.accessibility.LargePointerIconPreferenceController"/>
- <SwitchPreference
- android:key="toggle_high_text_contrast_preference"
- android:persistent="false"
- android:title="@string/accessibility_toggle_high_text_contrast_preference_title"
- settings:controller="com.android.settings.accessibility.HighTextContrastPreferenceController"/>
-
<PreferenceCategory
android:key="experimental_category"
android:persistent="false"
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index fcdf47b..22226a1 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -36,21 +36,19 @@
android:persistent="false"
android:title="@string/display_category_title">
- <!-- After completely finishing the Text and reading options feature, it will be visible. -->
<Preference
android:fragment="com.android.settings.accessibility.TextReadingPreferenceFragment"
android:icon="@drawable/ic_adaptive_font_download"
android:key="text_reading_options"
android:persistent="false"
- android:title="@string/accessibility_text_reading_options_title"
- settings:isPreferenceVisible="false"/>
+ android:title="@string/accessibility_text_reading_options_title" />
<Preference
- android:fragment="com.android.settings.accessibility.TextAndDisplayFragment"
- android:key="text_and_display_preference_screen"
- android:icon="@drawable/ic_text_and_display"
+ android:fragment="com.android.settings.accessibility.ColorAndMotionFragment"
+ android:key="color_and_motion"
+ android:icon="@drawable/ic_color_and_motion"
android:persistent="false"
- android:title="@string/accessibility_text_and_display_title"
+ android:title="@string/accessibility_color_and_motion_title"
settings:searchable="true"/>
<com.android.settingslib.PrimarySwitchPreference
diff --git a/res/xml/accessibility_settings_for_setup_wizard.xml b/res/xml/accessibility_settings_for_setup_wizard.xml
index 3272ae5..78d8f72 100644
--- a/res/xml/accessibility_settings_for_setup_wizard.xml
+++ b/res/xml/accessibility_settings_for_setup_wizard.xml
@@ -26,24 +26,9 @@
android:key="text_reading_options"
android:persistent="false"
android:title="@string/accessibility_text_reading_options_title"
- settings:isPreferenceVisible="false"
settings:keywords="text_reading_options" />
<Preference
- android:key="font_size_preference"
- android:icon="@drawable/ic_font_size"
- android:summary="@string/short_summary_font_size"
- android:title="@string/title_font_size"
- settings:controller="com.android.settings.accessibility.FontSizePreferenceController" />
-
- <com.android.settings.display.ScreenZoomPreference
- android:key="force_density_preference"
- android:icon="@drawable/ic_screen_zoom"
- android:summary="@string/screen_zoom_short_summary"
- android:title="@string/screen_zoom_title"
- settings:controller="com.android.settings.accessibility.ScreenSizePreferenceController" />
-
- <Preference
android:fragment="com.android.settings.accessibility.ToggleScreenMagnificationPreferenceFragmentForSetupWizard"
android:key="screen_magnification_preference"
android:icon="@drawable/ic_accessibility_magnification"
diff --git a/res/xml/accessibility_system_controls.xml b/res/xml/accessibility_system_controls.xml
index 71e1143..37c4d67 100644
--- a/res/xml/accessibility_system_controls.xml
+++ b/res/xml/accessibility_system_controls.xml
@@ -42,9 +42,22 @@
android:title="@string/accessibility_power_button_ends_call_prerefence_title"
settings:controller="com.android.settings.accessibility.PowerButtonEndsCallPreferenceController"/>
+ <!-- Standard auto-rotation preference that will be shown when device state based auto-rotation
+ settings are NOT available. -->
<SwitchPreference
android:key="toggle_lock_screen_rotation_preference"
android:persistent="false"
android:title="@string/accelerometer_title"
settings:controller="com.android.settings.accessibility.LockScreenRotationPreferenceController"/>
+
+ <!-- Auto-rotation preference that will be shown when device state based auto-rotation settings
+ are available. -->
+ <Preference
+ android:key="device_state_auto_rotate_accessibility"
+ android:persistent="false"
+ android:title="@string/accelerometer_title"
+ android:fragment="com.android.settings.display.DeviceStateAutoRotateDetailsFragment"
+ settings:keywords="@string/keywords_auto_rotate"
+ settings:controller="com.android.settings.display.DeviceStateAutoRotateOverviewController"/>
+
</PreferenceScreen>
diff --git a/res/xml/accessibility_text_reading_options.xml b/res/xml/accessibility_text_reading_options.xml
index d55882d..ad742c9 100644
--- a/res/xml/accessibility_text_reading_options.xml
+++ b/res/xml/accessibility_text_reading_options.xml
@@ -33,7 +33,8 @@
settings:iconEnd="@drawable/ic_add_24dp"
settings:iconEndContentDescription="@string/font_size_make_larger_desc"
settings:iconStart="@drawable/ic_remove_24dp"
- settings:iconStartContentDescription="@string/font_size_make_smaller_desc"/>
+ settings:iconStartContentDescription="@string/font_size_make_smaller_desc"
+ settings:keywords="@string/keywords_font_size" />
<com.android.settings.widget.LabeledSeekBarPreference
android:key="display_size"
@@ -43,7 +44,8 @@
settings:iconEnd="@drawable/ic_add_24dp"
settings:iconEndContentDescription="@string/screen_zoom_make_larger_desc"
settings:iconStart="@drawable/ic_remove_24dp"
- settings:iconStartContentDescription="@string/screen_zoom_make_smaller_desc"/>
+ settings:iconStartContentDescription="@string/screen_zoom_make_smaller_desc"
+ settings:keywords="@string/keywords_display_size" />
<SwitchPreference
android:key="toggle_force_bold_text"
diff --git a/res/xml/device_state_auto_rotate_settings.xml b/res/xml/device_state_auto_rotate_settings.xml
new file mode 100644
index 0000000..2ddb4c7
--- /dev/null
+++ b/res/xml/device_state_auto_rotate_settings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:title="@string/accelerometer_title" >
+
+ <!-- Device state based auto-rotation preferences will be added programmatically here. -->
+
+</PreferenceScreen>
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index 328e15c..ba52a30 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -69,17 +69,10 @@
settings:keywords="@string/keywords_dark_ui_mode"/>
<Preference
- android:key="font_size"
- android:title="@string/title_font_size"
- android:fragment="com.android.settings.display.ToggleFontSizePreferenceFragment"
- settings:controller="com.android.settings.display.FontSizePreferenceController"
- settings:keywords="@string/keywords_font_size"/>
-
- <com.android.settings.display.ScreenZoomPreference
- android:key="display_settings_screen_zoom"
- android:title="@string/screen_zoom_title"
- android:fragment="com.android.settings.display.ScreenZoomSettings"
- settings:keywords="@string/keywords_display_size"/>
+ android:fragment="com.android.settings.accessibility.TextReadingPreferenceFragment"
+ android:key="text_reading_options"
+ android:persistent="false"
+ android:title="@string/accessibility_text_reading_options_title" />
</PreferenceCategory>
<PreferenceCategory
@@ -103,12 +96,27 @@
<PreferenceCategory
android:title="@string/category_name_display_controls">
+ <!--
+ Standard auto-rotation preference that will be shown when device state based
+ auto-rotation settings are NOT available.
+ -->
<SwitchPreference
android:key="auto_rotate"
android:title="@string/accelerometer_title"
settings:keywords="@string/keywords_auto_rotate"
settings:controller="com.android.settings.display.AutoRotatePreferenceController"/>
+ <!--
+ Auto-rotation preference that will be shown when device state based auto-rotation
+ settings are available.
+ -->
+ <Preference
+ android:key="device_state_auto_rotate"
+ android:title="@string/accelerometer_title"
+ android:fragment="com.android.settings.display.DeviceStateAutoRotateDetailsFragment"
+ settings:keywords="@string/keywords_auto_rotate"
+ settings:controller="com.android.settings.display.DeviceStateAutoRotateOverviewController"/>
+
<SwitchPreference
android:key="display_white_balance"
android:title="@string/display_white_balance_title"
diff --git a/res/xml/dream_fragment_overview.xml b/res/xml/dream_fragment_overview.xml
index 9dfa105..105cec4 100644
--- a/res/xml/dream_fragment_overview.xml
+++ b/res/xml/dream_fragment_overview.xml
@@ -29,18 +29,12 @@
</PreferenceCategory>
<PreferenceCategory
- android:key="dream_complication_category"
- android:title="@string/dream_complications_picker_category"
- settings:controller="com.android.settings.dream.DreamComplicationCategoryController">
- <com.android.settingslib.widget.LayoutPreference
- android:key="dream_complication_picker"
- android:selectable="false"
- android:layout="@layout/dream_picker_layout"
- settings:controller="com.android.settings.dream.DreamComplicationPickerController"/>
- </PreferenceCategory>
-
- <PreferenceCategory
android:title="@string/dream_more_settings_category">
+ <SwitchPreference
+ android:key="dream_complications_toggle"
+ android:title="@string/dream_complications_toggle_title"
+ android:summary="@string/dream_complications_toggle_summary"
+ settings:controller="com.android.settings.dream.DreamComplicationPreferenceController"/>
<Preference
android:key="when_to_start"
android:title="@string/screensaver_settings_when_to_dream"
diff --git a/src/com/android/settings/RestrictedListPreference.java b/src/com/android/settings/RestrictedListPreference.java
index bd3cd17..d75f1b8 100644
--- a/src/com/android/settings/RestrictedListPreference.java
+++ b/src/com/android/settings/RestrictedListPreference.java
@@ -30,7 +30,6 @@
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
-import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
@@ -53,7 +52,6 @@
public RestrictedListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- setWidgetLayoutResource(R.layout.restricted_icon);
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
}
@@ -67,10 +65,6 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
- final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
- if (restrictedIcon != null) {
- restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
- }
}
@Override
@@ -187,11 +181,9 @@
View root = super.getView(position, convertView, parent);
CharSequence entry = getItem(position);
CheckedTextView text = (CheckedTextView) root.findViewById(R.id.text1);
- ImageView padlock = (ImageView) root.findViewById(R.id.restricted_lock_icon);
if (isRestrictedForEntry(entry)) {
text.setEnabled(false);
text.setChecked(false);
- padlock.setVisibility(View.VISIBLE);
} else {
if (mSelectedIndex != -1) {
text.setChecked(position == mSelectedIndex);
@@ -199,7 +191,6 @@
if (!text.isEnabled()) {
text.setEnabled(true);
}
- padlock.setVisibility(View.GONE);
}
return root;
}
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index e276484..57d7d10 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -33,7 +33,7 @@
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.security.SecuritySettingsFeatureProvider;
import com.google.android.setupdesign.util.ThemeHelper;
@@ -152,7 +152,7 @@
/** Redirects to SafetyCenter if enabled. */
@VisibleForTesting
public void handleSafetyCenterRedirection() {
- if (SafetyCenterStatusHolder.get().isEnabled(this)) {
+ if (SafetyCenterManagerWrapper.get().isEnabled(this)) {
try {
startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
finish();
@@ -213,7 +213,7 @@
/** Redirects to SafetyCenter if enabled. */
@VisibleForTesting
public void handleSafetyCenterRedirection() {
- if (SafetyCenterStatusHolder.get().isEnabled(this)) {
+ if (SafetyCenterManagerWrapper.get().isEnabled(this)) {
try {
startActivity(new Intent(Intent.ACTION_SAFETY_CENTER));
finish();
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index d3d3604..d9dc590 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -143,6 +143,8 @@
public static final String EXTRA_SHOW_FRAGMENT_AS_SUBSETTING =
":settings:show_fragment_as_subsetting";
+ public static final String EXTRA_IS_SECONDARY_LAYER_PAGE =
+ ":settings:is_secondary_layer_page";
/**
* Additional extra of Settings#ACTION_SETTINGS_LARGE_SCREEN_DEEP_LINK.
@@ -301,12 +303,12 @@
launchSettingFragment(initialFragmentName, intent);
}
- final boolean isInSetupWizard = WizardManagerHelper.isAnySetupWizard(getIntent());
+ final boolean isActionBarButtonEnabled = isActionBarButtonEnabled(intent);
final ActionBar actionBar = getActionBar();
if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(!isInSetupWizard);
- actionBar.setHomeButtonEnabled(!isInSetupWizard);
+ actionBar.setDisplayHomeAsUpEnabled(isActionBarButtonEnabled);
+ actionBar.setHomeButtonEnabled(isActionBarButtonEnabled);
actionBar.setDisplayShowTitleEnabled(true);
}
mMainSwitch = findViewById(R.id.switch_bar);
@@ -366,6 +368,18 @@
}
}
+ private boolean isActionBarButtonEnabled(Intent intent) {
+ if (WizardManagerHelper.isAnySetupWizard(intent)) {
+ return false;
+ }
+ final boolean isSecondaryLayerPage =
+ intent.getBooleanExtra(EXTRA_IS_SECONDARY_LAYER_PAGE, false);
+
+ // TODO: move Settings's ActivityEmbeddingUtils to SettingsLib.
+ return !com.android.settingslib.activityembedding.ActivityEmbeddingUtils
+ .shouldHideBackButton(this, isSecondaryLayerPage);
+ }
+
private boolean isSubSettings(Intent intent) {
return this instanceof SubSettings ||
intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index e485d1e..ae24168 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -34,6 +34,9 @@
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
+import android.net.EthernetManager.InterfaceState;
+import android.net.EthernetManager.Role;
+import android.net.IpConfiguration;
import android.net.TetheringManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
@@ -42,10 +45,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.SearchIndexableResource;
-import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
@@ -62,6 +65,7 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@@ -97,7 +101,6 @@
private BroadcastReceiver mTetherChangeReceiver;
private String[] mBluetoothRegexs;
- private String mEthernetRegex;
private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
private Handler mHandler = new Handler();
@@ -106,6 +109,7 @@
private EthernetManager mEm;
private TetheringEventCallback mTetheringEventCallback;
private EthernetListener mEthernetListener;
+ private final HashSet<String> mAvailableInterfaces = new HashSet<>();
private WifiTetherPreferenceController mWifiTetherPreferenceController;
@@ -172,17 +176,17 @@
mDataSaverBackend.addListener(this);
mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- mEm = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE);
mTm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE);
+ // Some devices do not have available EthernetManager. In that case getSystemService will
+ // return null.
+ mEm = mContext.getSystemService(EthernetManager.class);
mUsbRegexs = mTm.getTetherableUsbRegexs();
mBluetoothRegexs = mTm.getTetherableBluetoothRegexs();
- mEthernetRegex = mContext.getResources().getString(
- com.android.internal.R.string.config_ethernet_iface_regex);
final boolean usbAvailable = mUsbRegexs.length != 0;
final boolean bluetoothAvailable = adapter != null && mBluetoothRegexs.length != 0;
- final boolean ethernetAvailable = !TextUtils.isEmpty(mEthernetRegex);
+ final boolean ethernetAvailable = (mEm != null);
if (!usbAvailable || Utils.isMonkeyRunning()) {
getPreferenceScreen().removePreference(mUsbTether);
@@ -330,7 +334,7 @@
mEthernetListener = new EthernetListener();
if (mEm != null)
- mEm.addListener(mEthernetListener, r -> mHandler.post(r));
+ mEm.addInterfaceStateListener(r -> mHandler.post(r), mEthernetListener);
updateUsbState();
updateBluetoothAndEthernetState();
@@ -346,11 +350,10 @@
getActivity().unregisterReceiver(mTetherChangeReceiver);
mTm.unregisterTetheringEventCallback(mTetheringEventCallback);
if (mEm != null)
- mEm.removeListener(mEthernetListener);
+ mEm.removeInterfaceStateListener(mEthernetListener);
mTetherChangeReceiver = null;
mStartTetheringCallback = null;
mTetheringEventCallback = null;
- mEthernetListener = null;
}
@VisibleForTesting
@@ -483,11 +486,11 @@
boolean isTethered = false;
for (String s : available) {
- if (s.matches(mEthernetRegex)) isAvailable = true;
+ if (mAvailableInterfaces.contains(s)) isAvailable = true;
}
for (String s : tethered) {
- if (s.matches(mEthernetRegex)) isTethered = true;
+ if (mAvailableInterfaces.contains(s)) isTethered = true;
}
if (DEBUG) {
@@ -498,7 +501,7 @@
if (isTethered) {
mEthernetTether.setEnabled(!mDataSaverEnabled);
mEthernetTether.setChecked(true);
- } else if (isAvailable || (mEm != null && mEm.isAvailable())) {
+ } else if (mAvailableInterfaces.size() > 0) {
mEthernetTether.setEnabled(!mDataSaverEnabled);
mEthernetTether.setChecked(false);
} else {
@@ -600,9 +603,9 @@
keys.add(KEY_ENABLE_BLUETOOTH_TETHERING);
}
- final boolean ethernetAvailable = !TextUtils.isEmpty(
- context.getResources().getString(
- com.android.internal.R.string.config_ethernet_iface_regex));
+ final EthernetManager em =
+ context.getSystemService(EthernetManager.class);
+ final boolean ethernetAvailable = (em != null);
if (!ethernetAvailable) {
keys.add(KEY_ENABLE_ETHERNET_TETHERING);
}
@@ -646,9 +649,15 @@
}
}
- private final class EthernetListener implements EthernetManager.Listener {
- public void onAvailabilityChanged(String iface, boolean isAvailable) {
- mHandler.post(() -> updateBluetoothAndEthernetState());
+ private final class EthernetListener implements EthernetManager.InterfaceStateListener {
+ public void onInterfaceStateChanged(@NonNull String iface, @InterfaceState int state,
+ @Role int role, @NonNull IpConfiguration configuration) {
+ if (state == EthernetManager.STATE_LINK_UP) {
+ mAvailableInterfaces.add(iface);
+ } else {
+ mAvailableInterfaces.remove(iface);
+ }
+ updateBluetoothAndEthernetState();
}
}
}
diff --git a/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindow.java b/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindow.java
index f24934f..2c77102 100644
--- a/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindow.java
+++ b/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindow.java
@@ -27,10 +27,12 @@
import android.view.View.AccessibilityDelegate;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
+import androidx.annotation.DrawableRes;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
@@ -76,9 +78,10 @@
* Sets up {@link #AccessibilityQuickSettingsTooltipWindow}'s layout and content.
*
* @param text text to be displayed
+ * @param imageResId the resource ID of the image drawable
*/
- public void setup(String text) {
- this.setup(text, /* closeDelayTimeMillis= */ 0);
+ public void setup(String text, @DrawableRes int imageResId) {
+ this.setup(text, imageResId, /* closeDelayTimeMillis= */ 0);
}
/**
@@ -88,9 +91,10 @@
* close delay time is positive number. </p>
*
* @param text text to be displayed
+ * @param imageResId the resource ID of the image drawable
* @param closeDelayTimeMillis how long the popup window be auto-closed
*/
- public void setup(String text, long closeDelayTimeMillis) {
+ public void setup(String text, @DrawableRes int imageResId, long closeDelayTimeMillis) {
this.mCloseDelayTimeMillis = closeDelayTimeMillis;
setBackgroundDrawable(new ColorDrawable(mContext.getColor(android.R.color.transparent)));
@@ -101,6 +105,8 @@
popupView.setAccessibilityDelegate(mAccessibilityDelegate);
setContentView(popupView);
+ final ImageView imageView = getContentView().findViewById(R.id.qs_illustration);
+ imageView.setImageResource(imageResId);
final TextView textView = getContentView().findViewById(R.id.qs_content);
textView.setText(text);
setWidth(getWindowWidthWith(textView));
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
index 2b9729e..59618d6 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
@@ -18,6 +18,7 @@
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
+import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
@@ -41,6 +42,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.utils.LocaleUtils;
@@ -70,6 +72,7 @@
private CheckBox mHardwareTypeCheckBox;
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
+ private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
/** Returns the accessibility component name. */
protected abstract ComponentName getComponentName();
@@ -96,6 +99,9 @@
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
+ if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
+ mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
+ }
}
final int resId = getPreferenceScreenResId();
@@ -176,6 +182,7 @@
}
if (mTooltipWindow != null) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing());
+ outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
}
super.onSaveInstanceState(outState);
}
@@ -198,7 +205,7 @@
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
dialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
- getUserShortcutTypes());
+ getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
dialog.setCanceledOnTouchOutside(false);
return dialog;
default:
@@ -297,6 +304,17 @@
};
/**
+ * This method will be invoked when a button in the tutorial dialog is clicked.
+ *
+ * @param dialog The dialog that received the click
+ * @param which The button that was clicked
+ */
+ private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ showQuickSettingsTooltipIfNeeded();
+ }
+
+ /**
* This method will be invoked when a button in the edit shortcut dialog is clicked.
*
* @param dialog The dialog that received the click
@@ -308,12 +326,17 @@
}
final int value = getShortcutTypeCheckBoxValue();
-
saveNonEmptyUserShortcutType(value);
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, getComponentName());
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, getComponentName());
- mShortcutPreference.setChecked(value != AccessibilityUtil.UserShortcutType.EMPTY);
+ final boolean shortcutAssigned = value != AccessibilityUtil.UserShortcutType.EMPTY;
+ mShortcutPreference.setChecked(shortcutAssigned);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+
+ // Show the quick setting tooltip if the shortcut assigned in the first time
+ if (shortcutAssigned) {
+ showQuickSettingsTooltipIfNeeded();
+ }
}
@VisibleForTesting
@@ -452,7 +475,18 @@
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
- protected void showQuickSettingsTooltipIfNeeded() {
+ /**
+ * Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
+ * shows once.
+ *
+ * @param type The quick settings tooltip type
+ */
+ protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
+ mNeedsQSTooltipType = type;
+ showQuickSettingsTooltipIfNeeded();
+ }
+
+ private void showQuickSettingsTooltipIfNeeded() {
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
@@ -471,10 +505,15 @@
return;
}
- final String title =
- getString(R.string.accessibility_service_quick_settings_tooltips_content, tileName);
+ final int titleResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.string.accessibility_service_qs_tooltips_content
+ : R.string.accessibility_service_auto_added_qs_tooltips_content;
+ final String title = getString(titleResId, tileName);
+ final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.drawable.accessibility_qs_tooltips_illustration
+ : R.drawable.accessibility_auto_added_qs_tooltips_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
- mTooltipWindow.setup(title);
+ mTooltipWindow.setup(title, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index c935956..3e97edc 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -46,7 +46,7 @@
import java.util.StringJoiner;
/** Provides utility methods to accessibility settings only. */
-final class AccessibilityUtil {
+public final class AccessibilityUtil {
private AccessibilityUtil(){}
@@ -105,6 +105,17 @@
int TRIPLETAP = 4; // 1 << 2
}
+ /**
+ * Denotes the quick setting tooltip type.
+ *
+ * {@code GUIDE_TO_EDIT} for QS tiles that need to be added by editing.
+ * {@code GUIDE_TO_DIRECT_USE} for QS tiles that have been auto-added already.
+ */
+ public @interface QuickSettingsTooltipType {
+ int GUIDE_TO_EDIT = 0;
+ int GUIDE_TO_DIRECT_USE = 1;
+ }
+
/** Denotes the accessibility enabled status */
@Retention(RetentionPolicy.SOURCE)
public @interface State {
diff --git a/src/com/android/settings/accessibility/TextAndDisplayFragment.java b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
similarity index 91%
rename from src/com/android/settings/accessibility/TextAndDisplayFragment.java
rename to src/com/android/settings/accessibility/ColorAndMotionFragment.java
index e81dded..7f55053 100644
--- a/src/com/android/settings/accessibility/TextAndDisplayFragment.java
+++ b/src/com/android/settings/accessibility/ColorAndMotionFragment.java
@@ -30,11 +30,11 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
-/** Accessibility settings for text and display. */
+/** Accessibility settings for color and motion. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
-public class TextAndDisplayFragment extends DashboardFragment {
+public class ColorAndMotionFragment extends DashboardFragment {
- private static final String TAG = "TextAndDisplayFragment";
+ private static final String TAG = "ColorAndMotionFragment";
private static final String CATEGORY_EXPERIMENTAL = "experimental_category";
@@ -49,7 +49,7 @@
@Override
public int getMetricsCategory() {
- return SettingsEnums.ACCESSIBILITY_TEXT_AND_DISPLAY;
+ return SettingsEnums.ACCESSIBILITY_COLOR_AND_MOTION;
}
@@ -62,7 +62,7 @@
@Override
protected int getPreferenceScreenResId() {
- return R.xml.accessibility_text_and_display;
+ return R.xml.accessibility_color_and_motion;
}
@Override
@@ -104,5 +104,5 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.accessibility_text_and_display);
+ new BaseSearchIndexProvider(R.xml.accessibility_color_and_motion);
}
diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
index ce8db21..b04582f 100644
--- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
@@ -20,11 +20,9 @@
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.app.ActivityOptions;
-import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ContentResolver;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.net.Uri;
@@ -139,20 +137,6 @@
}
@Override
- public Dialog onCreateDialog(int dialogId) {
- switch (dialogId) {
- case AccessibilityDialogUtils.DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
- final Dialog dialog = AccessibilityGestureNavigationTutorial
- .createAccessibilityTutorialDialog(getPrefContext(),
- getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
- dialog.setCanceledOnTouchOutside(false);
- return dialog;
- default:
- return super.onCreateDialog(dialogId);
- }
- }
-
- @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Do not call super. We don't want to see the "Help & feedback" option on this page so as
// not to confuse users who think they might be able to send feedback about a specific
@@ -236,22 +220,4 @@
return settingsIntent;
}
-
- /**
- * This method will be invoked when a button in the tutorial dialog is clicked.
- *
- * @param dialog The dialog that received the click
- * @param which The button that was clicked
- */
- private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
- dialog.dismiss();
- showQuickSettingsTooltipIfNeeded();
- }
-
-
- @Override
- protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
- super.callOnAlertDialogCheckboxClicked(dialog, which);
- showQuickSettingsTooltipIfNeeded(getShortcutTypeCheckBoxValue());
- }
}
diff --git a/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java b/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
index 5ed4b5c..a1c292a 100644
--- a/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
+++ b/src/com/android/settings/accessibility/LockScreenRotationPreferenceController.java
@@ -25,6 +25,7 @@
import com.android.internal.view.RotationPolicy.RotationPolicyListener;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.display.DeviceStateAutoRotationHelper;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -59,7 +60,9 @@
@Override
public int getAvailabilityStatus() {
- return RotationPolicy.isRotationSupported(mContext) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ return RotationPolicy.isRotationSupported(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabledForA11y(mContext)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index d7a506a..02da51e 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -206,12 +206,6 @@
.createDisableDialog(getPrefContext(), info,
this::onDialogButtonFromDisableToggleClicked);
return mWarningDialog;
- case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
- final Dialog dialog = AccessibilityGestureNavigationTutorial
- .createAccessibilityTutorialDialog(getPrefContext(),
- getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
- dialog.setCanceledOnTouchOutside(false);
- return dialog;
default:
return super.onCreateDialog(dialogId);
}
@@ -496,23 +490,6 @@
mWarningDialog.dismiss();
}
- /**
- * This method will be invoked when a button in the tutorial dialog is clicked.
- *
- * @param dialog The dialog that received the click
- * @param which The button that was clicked
- */
- private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
- dialog.dismiss();
- showQuickSettingsTooltipIfNeeded();
- }
-
- @Override
- protected void callOnAlertDialogCheckboxClicked(DialogInterface dialog, int which) {
- super.callOnAlertDialogCheckboxClicked(dialog, which);
- showQuickSettingsTooltipIfNeeded(getShortcutTypeCheckBoxValue());
- }
-
void onDialogButtonFromShortcutClicked(View view) {
final int viewId = view.getId();
if (viewId == R.id.permission_enable_allow_button) {
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index e421626..1f498d6 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -33,6 +33,7 @@
import android.view.ViewGroup;
import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.widget.SettingsMainSwitchPreference;
import java.util.ArrayList;
@@ -50,7 +51,9 @@
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- super.onPreferenceToggled(preferenceKey, enabled);
+ if (enabled) {
+ showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
+ }
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
index b1b9f81..c4df5bf 100644
--- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@@ -35,6 +35,7 @@
import androidx.preference.Preference;
import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -165,7 +166,9 @@
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- super.onPreferenceToggled(preferenceKey, enabled);
+ if (enabled) {
+ showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
+ }
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 5747d0a..69b8f75 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -56,6 +56,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.accessibility.AccessibilityDialogUtils.DialogType;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.utils.LocaleUtils;
import com.android.settings.widget.SettingsMainSwitchBar;
@@ -78,6 +79,7 @@
public abstract class ToggleFeaturePreferenceFragment extends SettingsPreferenceFragment
implements ShortcutPreference.OnClickCallback, OnMainSwitchChangeListener {
+ protected TopIntroPreference mTopIntroPreference;
protected SettingsMainSwitchPreference mToggleServiceSwitchPreference;
protected ShortcutPreference mShortcutPreference;
protected Preference mSettingsPreference;
@@ -103,6 +105,7 @@
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_SAVED_USER_SHORTCUT_TYPE = "shortcut_type";
protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
+ protected static final String KEY_SAVED_QS_TOOLTIP_TYPE = "qs_tooltip_type";
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
@@ -113,6 +116,7 @@
private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private boolean mNeedsQSTooltipReshow = false;
+ private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
public static final int NOT_SET = -1;
// Save user's shortcutType value when savedInstance has value (e.g. device rotated).
@@ -148,6 +152,9 @@
if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
}
+ if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
+ mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
+ }
}
setupDefaultShortcutIfNecessary(getPrefContext());
@@ -254,6 +261,7 @@
}
if (mTooltipWindow != null) {
outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, mTooltipWindow.isShowing());
+ outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
}
super.onSaveInstanceState(outState);
}
@@ -274,7 +282,7 @@
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
mDialog = AccessibilityGestureNavigationTutorial
.createAccessibilityTutorialDialog(getPrefContext(),
- getUserShortcutTypes());
+ getUserShortcutTypes(), this::callOnTutorialDialogButtonClicked);
mDialog.setCanceledOnTouchOutside(false);
return mDialog;
default:
@@ -476,10 +484,10 @@
if (TextUtils.isEmpty(mTopIntroTitle)) {
return;
}
- final TopIntroPreference topIntroPreference = new TopIntroPreference(getPrefContext());
- topIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
- topIntroPreference.setTitle(mTopIntroTitle);
- getPreferenceScreen().addPreference(topIntroPreference);
+ mTopIntroPreference = new TopIntroPreference(getPrefContext());
+ mTopIntroPreference.setKey(KEY_TOP_INTRO_PREFERENCE);
+ mTopIntroPreference.setTitle(mTopIntroTitle);
+ getPreferenceScreen().addPreference(mTopIntroPreference);
}
private void initToggleServiceSwitchPreference() {
@@ -695,6 +703,17 @@
}
/**
+ * This method will be invoked when a button in the tutorial dialog is clicked.
+ *
+ * @param dialog The dialog that received the click
+ * @param which The button that was clicked
+ */
+ private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ showQuickSettingsTooltipIfNeeded();
+ }
+
+ /**
* This method will be invoked when a button in the edit shortcut dialog is clicked.
*
* @param dialog The dialog that received the click
@@ -706,12 +725,17 @@
}
final int value = getShortcutTypeCheckBoxValue();
-
saveNonEmptyUserShortcutType(value);
AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), value, mComponentName);
AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), ~value, mComponentName);
- mShortcutPreference.setChecked(value != UserShortcutType.EMPTY);
+ final boolean shortcutAssigned = value != UserShortcutType.EMPTY;
+ mShortcutPreference.setChecked(shortcutAssigned);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+
+ // Show the quick setting tooltip if the shortcut assigned in the first time
+ if (shortcutAssigned) {
+ showQuickSettingsTooltipIfNeeded();
+ }
}
protected void updateShortcutPreferenceData() {
@@ -823,23 +847,17 @@
}
/**
- * Shows the quick settings tooltip if the quick settings service and the shortcut are assigned.
- * The tooltip only shows once.
+ * Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
+ * shows once.
*
- * @param shortcutType The shortcut type.
+ * @param type The quick settings tooltip type
*/
- protected void showQuickSettingsTooltipIfNeeded(@UserShortcutType int shortcutType) {
- if (shortcutType == AccessibilityUtil.UserShortcutType.EMPTY) {
- return;
- }
+ protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
+ mNeedsQSTooltipType = type;
showQuickSettingsTooltipIfNeeded();
}
- /**
- * Shows the quick settings tooltip if the quick settings service is assigned. The tooltip only
- * shows once.
- */
- protected void showQuickSettingsTooltipIfNeeded() {
+ private void showQuickSettingsTooltipIfNeeded() {
final ComponentName tileComponentName = getTileComponentName();
if (tileComponentName == null) {
// Returns if no tile service assigned.
@@ -858,10 +876,15 @@
return;
}
- final String title =
- getString(R.string.accessibility_service_quick_settings_tooltips_content, tileName);
+ final int titleResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.string.accessibility_service_qs_tooltips_content
+ : R.string.accessibility_service_auto_added_qs_tooltips_content;
+ final String title = getString(titleResId, tileName);
+ final int imageResId = mNeedsQSTooltipType == QuickSettingsTooltipType.GUIDE_TO_EDIT
+ ? R.drawable.accessibility_qs_tooltips_illustration
+ : R.drawable.accessibility_auto_added_qs_tooltips_illustration;
mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(getContext());
- mTooltipWindow.setup(title);
+ mTooltipWindow.setup(title, imageResId);
mTooltipWindow.showAtTopCenter(getView());
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(getContext(),
tileComponentName);
diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
index 1e2e9e5..fb9c4a8 100644
--- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
@@ -36,6 +36,7 @@
import androidx.preference.SwitchPreference;
import com.android.settings.R;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SeekBarPreference;
import com.android.settings.widget.SettingsMainSwitchPreference;
@@ -147,7 +148,9 @@
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- super.onPreferenceToggled(preferenceKey, enabled);
+ if (enabled) {
+ showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
+ }
logAccessibilityServiceEnabled(mComponentName, enabled);
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
}
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
index d92fd51..cb5ca75 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
@@ -40,7 +40,7 @@
final String title = getContext().getString(
R.string.accessibility_screen_magnification_title);
final String description = getContext().getString(
- R.string.accessibility_preference_magnification_summary);
+ R.string.accessibility_screen_magnification_intro_text);
final Drawable icon = getContext().getDrawable(R.drawable.ic_accessibility_visibility);
AccessibilitySetupWizardUtils.updateGlifPreferenceLayout(getContext(), layout, title,
description, icon);
@@ -51,6 +51,8 @@
* Hide the magnification preference settings in the SuW's vision settings.
*/
private void hidePreferenceSettingComponents() {
+ // Intro
+ mTopIntroPreference.setVisible(false);
// Setting of magnification type
mSettingsPreference.setVisible(false);
// Setting of following typing
diff --git a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
index 7adddf9..f9a1113 100644
--- a/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenReaderPreferenceFragmentForSetupWizard.java
@@ -46,6 +46,9 @@
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
+ if (mTopIntroPreference != null) {
+ mTopIntroPreference.setVisible(false);
+ }
}
@Override
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index b5c1123..a460419 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -46,6 +46,9 @@
description, icon);
mToggleSwitchWasInitiallyChecked = mToggleServiceSwitchPreference.isChecked();
+ if (mTopIntroPreference != null) {
+ mTopIntroPreference.setVisible(false);
+ }
}
@Override
diff --git a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
index a2dd4bf..dee24df 100644
--- a/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
+++ b/src/com/android/settings/activityembedding/ActivityEmbeddingRulesController.java
@@ -34,6 +34,7 @@
import androidx.window.embedding.SplitRule;
import com.android.settings.Settings;
+import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.biometrics.fingerprint.FingerprintEnrollIntroduction;
@@ -196,6 +197,7 @@
addActivityFilter(activityFilters, Settings.class);
final Intent intent = new Intent(mContext, Settings.NetworkDashboardActivity.class);
+ intent.putExtra(SettingsActivity.EXTRA_IS_SECONDARY_LAYER_PAGE, true);
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule(
activityFilters,
intent,
diff --git a/src/com/android/settings/applications/ProcessStatsBase.java b/src/com/android/settings/applications/ProcessStatsBase.java
index 67fc4c1..ce1453f 100644
--- a/src/com/android/settings/applications/ProcessStatsBase.java
+++ b/src/com/android/settings/applications/ProcessStatsBase.java
@@ -30,7 +30,7 @@
import com.android.settings.applications.ProcStatsData.MemInfo;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
public abstract class ProcessStatsBase extends SettingsPreferenceFragment
implements OnItemSelectedListener {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 0c12706..a6ce6fb 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -135,7 +135,7 @@
import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import com.google.android.material.appbar.AppBarLayout;
diff --git a/src/com/android/settings/biometrics/BiometricNavigationUtils.java b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
new file mode 100644
index 0000000..e4f2b7f
--- /dev/null
+++ b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.settings.Utils;
+import com.android.settings.core.SettingsBaseActivity;
+import com.android.settingslib.transition.SettingsTransitionHelper;
+
+/**
+ * Utilities for navigation shared between Security Settings and Safety Center.
+ */
+public class BiometricNavigationUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+
+ /**
+ * Tries to launch the Settings screen if Quiet Mode is not enabled
+ * for managed profile, otherwise shows a dialog to disable the Quiet Mode.
+ *
+ * @param className The class name of Settings screen to launch.
+ * @param extras Extras to put into the launching {@link Intent}.
+ * @return true if the Settings screen is launching.
+ */
+ public boolean launchBiometricSettings(Context context, String className, Bundle extras) {
+ final UserManager userManager = UserManager.get(context);
+ if (Utils.startQuietModeDialogIfNecessary(context, userManager, mUserId)) {
+ return false;
+ }
+
+ final Intent intent = new Intent();
+ intent.setClassName(SETTINGS_PACKAGE_NAME, className);
+ if (!extras.isEmpty()) {
+ intent.putExtras(extras);
+ }
+ intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
+ intent.putExtra(Intent.EXTRA_USER_ID, mUserId);
+ intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
+ SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
+ context.startActivity(intent);
+ return true;
+ }
+}
diff --git a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
index 801dd03..617529f 100644
--- a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
@@ -16,11 +16,7 @@
package com.android.settings.biometrics;
-import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
-import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
-
import android.content.Context;
-import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -30,9 +26,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.transition.SettingsTransitionHelper;
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
@@ -42,36 +36,23 @@
private final int mUserId = UserHandle.myUserId();
protected final int mProfileChallengeUserId;
+ private final BiometricNavigationUtils mBiometricNavigationUtils;
+
/**
* @return true if the manager is not null and the hardware is detected.
*/
protected abstract boolean isDeviceSupported();
/**
- * @return true if the user has enrolled biometrics of the subclassed type.
+ * @return the summary text.
*/
- protected abstract boolean hasEnrolledBiometrics();
-
- /**
- * @return the summary text if biometrics are enrolled.
- */
- protected abstract String getSummaryTextEnrolled();
-
- /**
- * @return the summary text if no biometrics are enrolled.
- */
- protected abstract String getSummaryTextNoneEnrolled();
+ protected abstract String getSummaryText();
/**
* @return the class name for the settings page.
*/
protected abstract String getSettingsClassName();
- /**
- * @return the class name for entry to enrollment.
- */
- protected abstract String getEnrollClassName();
-
public BiometricStatusPreferenceController(Context context, String key) {
super(context, key);
mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -79,6 +60,7 @@
.getSecurityFeatureProvider()
.getLockPatternUtils(context);
mProfileChallengeUserId = Utils.getManagedProfileId(mUm, mUserId);
+ mBiometricNavigationUtils = new BiometricNavigationUtils();
}
@Override
@@ -103,8 +85,7 @@
} else {
preference.setVisible(true);
}
- preference.setSummary(hasEnrolledBiometrics() ? getSummaryTextEnrolled()
- : getSummaryTextNoneEnrolled());
+ preference.setSummary(getSummaryText());
}
@Override
@@ -113,26 +94,8 @@
return super.handlePreferenceTreeClick(preference);
}
- final Context context = preference.getContext();
- final UserManager userManager = UserManager.get(context);
- final int userId = getUserId();
- if (Utils.startQuietModeDialogIfNecessary(context, userManager, userId)) {
- return false;
- }
-
- final Intent intent = new Intent();
- final String clazz = hasEnrolledBiometrics() ? getSettingsClassName()
- : getEnrollClassName();
- intent.setClassName(SETTINGS_PACKAGE_NAME, clazz);
- if (!preference.getExtras().isEmpty()) {
- intent.putExtras(preference.getExtras());
- }
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
- intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
- SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE);
- context.startActivity(intent);
- return true;
+ return mBiometricNavigationUtils.launchBiometricSettings(
+ preference.getContext(), getSettingsClassName(), preference.getExtras());
}
protected int getUserId() {
diff --git a/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
index 3ac14a1..800139c 100644
--- a/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/BiometricFaceStatusPreferenceController.java
@@ -29,7 +29,7 @@
public class BiometricFaceStatusPreferenceController extends FaceStatusPreferenceController {
public BiometricFaceStatusPreferenceController(Context context, String key) {
- super(context, key);
+ super(context, key, null /* lifecycle */);
}
public BiometricFaceStatusPreferenceController(
diff --git a/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
index 782d889..be19cb5 100644
--- a/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/BiometricFingerprintStatusPreferenceController.java
@@ -30,7 +30,7 @@
FingerprintStatusPreferenceController {
public BiometricFingerprintStatusPreferenceController(Context context, String key) {
- super(context, key);
+ super(context, key, null /* lifecycle */);
}
public BiometricFingerprintStatusPreferenceController(
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
index ddc69e1..b8706a5 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricProfileStatusPreferenceController.java
@@ -20,8 +20,6 @@
import androidx.lifecycle.Lifecycle;
-import com.android.settings.Settings;
-
/**
* Preference controller for biometrics settings page of work profile, controlling the ability to
* unlock the phone with face and fingerprint.
@@ -62,11 +60,6 @@
@Override
protected String getSettingsClassName() {
- return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
+ return mCombinedBiometricStatusUtils.getProfileSettingsClassName();
}
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
index 32fb3a0..27e3ae7 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceController.java
@@ -16,9 +16,6 @@
package com.android.settings.biometrics.combination;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
@@ -28,11 +25,7 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.Settings;
-import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -44,12 +37,9 @@
BiometricStatusPreferenceController implements LifecycleObserver {
private static final String KEY_BIOMETRIC_SETTINGS = "biometric_settings";
- @Nullable
- FingerprintManager mFingerprintManager;
- @Nullable
- FaceManager mFaceManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ protected final CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
public CombinedBiometricStatusPreferenceController(Context context) {
this(context, KEY_BIOMETRIC_SETTINGS, null /* lifecycle */);
@@ -66,8 +56,7 @@
public CombinedBiometricStatusPreferenceController(
Context context, String key, Lifecycle lifecycle) {
super(context, key);
- mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
- mFaceManager = Utils.getFaceManagerOrNull(context);
+ mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -87,12 +76,7 @@
@Override
protected boolean isDeviceSupported() {
- return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return false;
+ return mCombinedBiometricStatusUtils.isAvailable();
}
@Override
@@ -102,80 +86,35 @@
}
private void updateStateInternal() {
- // This controller currently is shown if fingerprint&face exist on the device. If this
- // changes in the future, the modalities passed into the below will need to be updated.
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mCombinedBiometricStatusUtils.getDisablingAdmin();
- final RestrictedLockUtils.EnforcedAdmin faceAdmin = ParentalControlsUtils
- .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
- final RestrictedLockUtils.EnforcedAdmin fpAdmin = ParentalControlsUtils
- .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
-
- // If the admins are non-null, they are actually always the same. Just the helper class
- // we create above always return the admin, instead of a boolean.
- final boolean faceConsentRequired = faceAdmin != null;
- final boolean fpConsentRequired = fpAdmin != null;
- final RestrictedLockUtils.EnforcedAdmin admin = faceAdmin != null ? faceAdmin : fpAdmin;
-
- updateStateInternal(admin, faceConsentRequired, fpConsentRequired);
+ updateStateInternal(admin);
}
+ /**
+ * Disables the preference and shows the consent flow only if consent is required for all
+ * modalities.
+ *
+ * <p>Otherwise, users will not be able to enter and modify settings for modalities which have
+ * already been consented. In any case, the controllers for the modalities which have not yet
+ * been consented will be disabled in the combined page anyway - users can go through the
+ * consent+enrollment flow from there.
+ */
@VisibleForTesting
- void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin,
- boolean faceConsentRequired, boolean fpConsentRequired) {
- // Disable the preference (and show the consent flow) only if consent is required for all
- // modalities. Otherwise, users will not be able to enter and modify settings for modalities
- // which have already been consented. In any case, the controllers for the modalities which
- // have not yet been consented will be disabled in the combined page anyway - users can
- // go through the consent+enrollment flow from there.
- final boolean disablePreference = faceConsentRequired && fpConsentRequired;
- if (!disablePreference) {
- enforcedAdmin = null;
- }
-
+ void updateStateInternal(@Nullable RestrictedLockUtils.EnforcedAdmin enforcedAdmin) {
if (mPreference != null) {
mPreference.setDisabledByAdmin(enforcedAdmin);
}
}
@Override
- protected String getSummaryTextEnrolled() {
- // Note that this is currently never called (see the super class)
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_none_enrolled);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- final int numFingerprintsEnrolled = mFingerprintManager != null ?
- mFingerprintManager.getEnrolledFingerprints(getUserId()).size() : 0;
- final boolean faceEnrolled = mFaceManager != null
- && mFaceManager.hasEnrolledTemplates(getUserId());
-
- if (faceEnrolled && numFingerprintsEnrolled > 1) {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_both_fp_multiple);
- } else if (faceEnrolled && numFingerprintsEnrolled == 1) {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_both_fp_single);
- } else if (faceEnrolled) {
- return mContext.getString(R.string.security_settings_face_preference_summary);
- } else if (numFingerprintsEnrolled > 0) {
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numFingerprintsEnrolled, numFingerprintsEnrolled);
- } else {
- return mContext.getString(
- R.string.security_settings_biometric_preference_summary_none_enrolled);
- }
+ protected String getSummaryText() {
+ return mCombinedBiometricStatusUtils.getSummary();
}
@Override
protected String getSettingsClassName() {
- return Settings.CombinedBiometricSettingsActivity.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return Settings.CombinedBiometricSettingsActivity.class.getName();
+ return mCombinedBiometricStatusUtils.getSettingsClassName();
}
}
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
new file mode 100644
index 0000000..a7554c8
--- /dev/null
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2022 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.biometrics.combination;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for combined biometric details shared between Security Settings and Safety Center.
+ */
+public class CombinedBiometricStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ @Nullable
+ FingerprintManager mFingerprintManager;
+ @Nullable
+ FaceManager mFaceManager;
+
+ public CombinedBiometricStatusUtils(Context context) {
+ mContext = context;
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ mFaceManager = Utils.getFaceManagerOrNull(context);
+ }
+
+ /**
+ * Returns whether the combined biometric settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return Utils.hasFingerprintHardware(mContext) && Utils.hasFaceHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} in case parental consent is required to change both
+ * face and fingerprint settings.
+ *
+ * @return null if either face or fingerprint settings do not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ // This controller currently is shown if fingerprint&face exist on the device. If this
+ // changes in the future, the modalities passed into the below will need to be updated.
+
+ final EnforcedAdmin faceAdmin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FACE);
+ final EnforcedAdmin fpAdmin = ParentalControlsUtils
+ .parentConsentRequired(mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
+
+ final boolean faceConsentRequired = faceAdmin != null;
+ final boolean fpConsentRequired = fpAdmin != null;
+
+ // Result is only required if all modalities require consent.
+ // If the admins are non-null, they are actually always the same.
+ return faceConsentRequired && fpConsentRequired ? faceAdmin : null;
+ }
+
+ /**
+ * Returns the summary of combined biometric settings entity.
+ */
+ public String getSummary() {
+ final int numFingerprintsEnrolled = mFingerprintManager != null
+ ? mFingerprintManager.getEnrolledFingerprints(mUserId).size() : 0;
+ final boolean faceEnrolled = mFaceManager != null
+ && mFaceManager.hasEnrolledTemplates(mUserId);
+
+ if (faceEnrolled && numFingerprintsEnrolled > 1) {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_both_fp_multiple);
+ } else if (faceEnrolled && numFingerprintsEnrolled == 1) {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_both_fp_single);
+ } else if (faceEnrolled) {
+ return mContext.getString(R.string.security_settings_face_preference_summary);
+ } else if (numFingerprintsEnrolled > 0) {
+ return mContext.getResources().getQuantityString(
+ R.plurals.security_settings_fingerprint_preference_summary,
+ numFingerprintsEnrolled, numFingerprintsEnrolled);
+ } else {
+ return mContext.getString(
+ R.string.security_settings_biometric_preference_summary_none_enrolled);
+ }
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to combined biometric settings.
+ */
+ public String getSettingsClassName() {
+ return Settings.CombinedBiometricSettingsActivity.class.getName();
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to combined biometric settings
+ * for work profile.
+ */
+ public String getProfileSettingsClassName() {
+ return Settings.CombinedBiometricProfileSettingsActivity.class.getName();
+ }
+}
diff --git a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
index 319166e..cd0bc15 100644
--- a/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/face/FaceStatusPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.biometrics.face;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.face.FaceManager;
import androidx.annotation.Nullable;
@@ -28,11 +27,8 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
-import com.android.settings.Settings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -44,6 +40,7 @@
protected final FaceManager mFaceManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ private final FaceStatusUtils mFaceStatusUtils;
public FaceStatusPreferenceController(Context context) {
this(context, KEY_FACE_SETTINGS, null /* lifecycle */);
@@ -60,6 +57,7 @@
public FaceStatusPreferenceController(Context context, String key, Lifecycle lifecycle) {
super(context, key);
mFaceManager = Utils.getFaceManagerOrNull(context);
+ mFaceStatusUtils = new FaceStatusUtils(context, mFaceManager);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -79,12 +77,7 @@
@Override
protected boolean isDeviceSupported() {
- return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return mFaceManager.hasEnrolledTemplates(getUserId());
+ return mFaceStatusUtils.isAvailable();
}
@Override
@@ -94,8 +87,7 @@
}
private void updateStateInternal() {
- updateStateInternal(ParentalControlsUtils.parentConsentRequired(
- mContext, BiometricAuthenticator.TYPE_FACE));
+ updateStateInternal(mFaceStatusUtils.getDisablingAdmin());
}
@VisibleForTesting
@@ -106,25 +98,12 @@
}
@Override
- protected String getSummaryTextEnrolled() {
- return mContext.getResources()
- .getString(R.string.security_settings_face_preference_summary);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- return mContext.getResources()
- .getString(R.string.security_settings_face_preference_summary_none);
+ protected String getSummaryText() {
+ return mFaceStatusUtils.getSummary();
}
@Override
protected String getSettingsClassName() {
- return Settings.FaceSettingsActivity.class.getName();
+ return mFaceStatusUtils.getSettingsClassName();
}
-
- @Override
- protected String getEnrollClassName() {
- return FaceEnrollIntroduction.class.getName();
- }
-
}
diff --git a/src/com/android/settings/biometrics/face/FaceStatusUtils.java b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
new file mode 100644
index 0000000..dd32708
--- /dev/null
+++ b/src/com/android/settings/biometrics/face/FaceStatusUtils.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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.biometrics.face;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.face.FaceManager;
+import android.os.UserHandle;
+
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for face details shared between Security Settings and Safety Center.
+ */
+public class FaceStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ private final FaceManager mFaceManager;
+
+ public FaceStatusUtils(Context context, FaceManager faceManager) {
+ mContext = context;
+ mFaceManager = faceManager;
+ }
+
+ /**
+ * Returns whether the face settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return !Utils.isMultipleBiometricsSupported(mContext) && Utils.hasFaceHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} if parental consent is required to change face settings.
+ *
+ * @return null if face settings does not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ return ParentalControlsUtils.parentConsentRequired(
+ mContext, BiometricAuthenticator.TYPE_FACE);
+ }
+
+ /**
+ * Returns the summary of face settings entity.
+ */
+ public String getSummary() {
+ return mContext.getResources().getString(hasEnrolled()
+ ? R.string.security_settings_face_preference_summary
+ : R.string.security_settings_face_preference_summary_none);
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to face settings.
+ */
+ public String getSettingsClassName() {
+ return hasEnrolled() ? Settings.FaceSettingsActivity.class.getName()
+ : FaceEnrollIntroduction.class.getName();
+ }
+
+ private boolean hasEnrolled() {
+ return mFaceManager.hasEnrolledTemplates(mUserId);
+ }
+}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
index 76c809c..646af4d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.biometrics.fingerprint;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.fingerprint.FingerprintManager;
import androidx.annotation.Nullable;
@@ -28,10 +27,8 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
-import com.android.settings.biometrics.ParentalControlsUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
@@ -43,6 +40,7 @@
protected final FingerprintManager mFingerprintManager;
@VisibleForTesting
RestrictedPreference mPreference;
+ private final FingerprintStatusUtils mFingerprintStatusUtils;
public FingerprintStatusPreferenceController(Context context) {
this(context, KEY_FINGERPRINT_SETTINGS);
@@ -59,6 +57,8 @@
public FingerprintStatusPreferenceController(Context context, String key, Lifecycle lifecycle) {
super(context, key);
mFingerprintManager = Utils.getFingerprintManagerOrNull(context);
+ mFingerprintStatusUtils =
+ new FingerprintStatusUtils(context, mFingerprintManager);
if (lifecycle != null) {
lifecycle.addObserver(this);
@@ -78,13 +78,7 @@
@Override
protected boolean isDeviceSupported() {
- return !Utils.isMultipleBiometricsSupported(mContext)
- && Utils.hasFingerprintHardware(mContext);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics() {
- return mFingerprintManager.hasEnrolledFingerprints(getUserId());
+ return mFingerprintStatusUtils.isAvailable();
}
@Override
@@ -94,8 +88,17 @@
}
private void updateStateInternal() {
- updateStateInternal(ParentalControlsUtils.parentConsentRequired(
- mContext, BiometricAuthenticator.TYPE_FINGERPRINT));
+ updateStateInternal(mFingerprintStatusUtils.getDisablingAdmin());
+ }
+
+ @Override
+ protected String getSummaryText() {
+ return mFingerprintStatusUtils.getSummary();
+ }
+
+ @Override
+ protected String getSettingsClassName() {
+ return mFingerprintStatusUtils.getSettingsClassName();
}
@VisibleForTesting
@@ -104,28 +107,4 @@
mPreference.setDisabledByAdmin(enforcedAdmin);
}
}
-
- @Override
- protected String getSummaryTextEnrolled() {
- final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(getUserId()).size();
- return mContext.getResources().getQuantityString(
- R.plurals.security_settings_fingerprint_preference_summary,
- numEnrolled, numEnrolled);
- }
-
- @Override
- protected String getSummaryTextNoneEnrolled() {
- return mContext.getString(R.string.security_settings_fingerprint_preference_summary_none);
- }
-
- @Override
- protected String getSettingsClassName() {
- return FingerprintSettings.class.getName();
- }
-
- @Override
- protected String getEnrollClassName() {
- return FingerprintEnrollIntroduction.class.getName();
- }
-
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
new file mode 100644
index 0000000..36edd2e
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtils.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.biometrics.fingerprint;
+
+import android.content.Context;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.biometrics.ParentalControlsUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+/**
+ * Utilities for fingerprint details shared between Security Settings and Safety Center.
+ */
+public class FingerprintStatusUtils {
+
+ private final int mUserId = UserHandle.myUserId();
+ private final Context mContext;
+ private final FingerprintManager mFingerprintManager;
+
+ public FingerprintStatusUtils(Context context, FingerprintManager fingerprintManager) {
+ mContext = context;
+ mFingerprintManager = fingerprintManager;
+ }
+
+ /**
+ * Returns whether the fingerprint settings entity should be shown.
+ */
+ public boolean isAvailable() {
+ return !Utils.isMultipleBiometricsSupported(mContext)
+ && Utils.hasFingerprintHardware(mContext);
+ }
+
+ /**
+ * Returns the {@link EnforcedAdmin} if parental consent is required to change face settings.
+ *
+ * @return null if face settings does not require a parental consent.
+ */
+ public EnforcedAdmin getDisablingAdmin() {
+ return ParentalControlsUtils.parentConsentRequired(
+ mContext, BiometricAuthenticator.TYPE_FINGERPRINT);
+ }
+
+ /**
+ * Returns the summary of fingerprint settings entity.
+ */
+ public String getSummary() {
+ if (hasEnrolled()) {
+ final int numEnrolled = mFingerprintManager.getEnrolledFingerprints(mUserId).size();
+ return mContext.getResources().getQuantityString(
+ R.plurals.security_settings_fingerprint_preference_summary,
+ numEnrolled, numEnrolled);
+ } else {
+ return mContext.getString(
+ R.string.security_settings_fingerprint_preference_summary_none);
+ }
+ }
+
+ /**
+ * Returns the class name of the Settings page corresponding to fingerprint settings.
+ */
+ public String getSettingsClassName() {
+ return hasEnrolled() ? FingerprintSettings.class.getName()
+ : FingerprintEnrollIntroduction.class.getName();
+ }
+
+ private boolean hasEnrolled() {
+ return mFingerprintManager.hasEnrolledFingerprints(mUserId);
+ }
+}
diff --git a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
index b60f1b6..1c12c6a 100644
--- a/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderController.java
@@ -99,6 +99,10 @@
@VisibleForTesting
boolean mIsRegisterCallback = false;
@VisibleForTesting
+ boolean mIsLeftDeviceEstimateReady;
+ @VisibleForTesting
+ boolean mIsRightDeviceEstimateReady;
+ @VisibleForTesting
final BluetoothAdapter.OnMetadataChangedListener mMetadataListener =
new BluetoothAdapter.OnMetadataChangedListener() {
@Override
@@ -226,6 +230,8 @@
BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING,
R.string.bluetooth_right_name,
RIGHT_DEVICE_ID);
+
+ showBothDevicesBatteryPredictionIfNecessary();
}
}
}
@@ -365,8 +371,13 @@
+ ", ESTIMATE_READY : " + estimateReady
+ ", BATTERY_ESTIMATE : " + batteryEstimate);
}
- showBatteryPredictionIfNecessary(estimateReady, batteryEstimate,
- linearLayout);
+
+ showBatteryPredictionIfNecessary(estimateReady, batteryEstimate, linearLayout);
+ if (batteryId == LEFT_DEVICE_ID) {
+ mIsLeftDeviceEstimateReady = estimateReady == 1;
+ } else if (batteryId == RIGHT_DEVICE_ID) {
+ mIsRightDeviceEstimateReady = estimateReady == 1;
+ }
}
} finally {
cursor.close();
@@ -380,7 +391,6 @@
ThreadUtils.postOnMainThread(() -> {
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
if (estimateReady == 1) {
- textView.setVisibility(View.VISIBLE);
textView.setText(
StringUtil.formatElapsedTime(
mContext,
@@ -393,6 +403,24 @@
});
}
+ @VisibleForTesting
+ void showBothDevicesBatteryPredictionIfNecessary() {
+ TextView leftDeviceTextView =
+ mLayoutPreference.findViewById(R.id.layout_left)
+ .findViewById(R.id.bt_battery_prediction);
+ TextView rightDeviceTextView =
+ mLayoutPreference.findViewById(R.id.layout_right)
+ .findViewById(R.id.bt_battery_prediction);
+
+ boolean isBothDevicesEstimateReady =
+ mIsLeftDeviceEstimateReady && mIsRightDeviceEstimateReady;
+ int visibility = isBothDevicesEstimateReady ? View.VISIBLE : View.GONE;
+ ThreadUtils.postOnMainThread(() -> {
+ leftDeviceTextView.setVisibility(visibility);
+ rightDeviceTextView.setVisibility(visibility);
+ });
+ }
+
private void showBatteryIcon(LinearLayout linearLayout, int level, int lowBatteryLevel,
boolean charging) {
final boolean enableLowBattery = level <= lowBatteryLevel && !charging;
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java b/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
index ec3e11f..d80512e 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsMacAddressController.java
@@ -46,21 +46,22 @@
protected void init(PreferenceScreen screen) {
mFooterPreference = screen.findPreference(KEY_DEVICE_DETAILS_FOOTER);
mFooterPreference.setTitle(mContext.getString(
- R.string.bluetooth_device_mac_address, mCachedDevice.getAddress()));
+ R.string.bluetooth_device_mac_address, mCachedDevice.getIdentityAddress()));
}
@Override
protected void refresh() {
if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
StringBuilder title = new StringBuilder(mContext.getString(
- R.string.bluetooth_multuple_devices_mac_address, mCachedDevice.getAddress()));
+ R.string.bluetooth_multuple_devices_mac_address,
+ mCachedDevice.getIdentityAddress()));
for (CachedBluetoothDevice member: mCachedDevice.getMemberDevice()) {
- title.append("\n").append(member.getAddress());
+ title.append("\n").append(member.getIdentityAddress());
}
mFooterPreference.setTitle(title);
} else {
mFooterPreference.setTitle(mContext.getString(
- R.string.bluetooth_device_mac_address, mCachedDevice.getAddress()));
+ R.string.bluetooth_device_mac_address, mCachedDevice.getIdentityAddress()));
}
}
diff --git a/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java b/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java
index bbaaa31..c440499 100644
--- a/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java
+++ b/src/com/android/settings/connecteddevice/usb/UsbDetailsTranscodeMtpController.java
@@ -77,6 +77,12 @@
public boolean onPreferenceClick(Preference preference) {
SystemProperties.set(TRANSCODE_MTP_SYS_PROP_KEY,
Boolean.toString(mSwitchPreference.isChecked()));
+
+ final long previousFunctions = mUsbBackend.getCurrentFunctions();
+ // Toggle the MTP connection to reload file sizes for files shared via MTP clients
+ mUsbBackend.setCurrentFunctions(previousFunctions & ~UsbManager.FUNCTION_MTP);
+ mUsbBackend.setCurrentFunctions(previousFunctions);
+
return true;
}
diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java
index 616d72f3..93a939f 100644
--- a/src/com/android/settings/core/SubSettingLauncher.java
+++ b/src/com/android/settings/core/SubSettingLauncher.java
@@ -43,11 +43,11 @@
}
mContext = context;
mLaunchRequest = new LaunchRequest();
- mLaunchRequest.transitionType = TransitionType.TRANSITION_SHARED_AXIS;
+ mLaunchRequest.mTransitionType = TransitionType.TRANSITION_SHARED_AXIS;
}
public SubSettingLauncher setDestination(String fragmentName) {
- mLaunchRequest.destinationName = fragmentName;
+ mLaunchRequest.mDestinationName = fragmentName;
return this;
}
@@ -67,9 +67,9 @@
* @param titleResId res id of string, will use package name to resolve
*/
public SubSettingLauncher setTitleRes(String titlePackageName, @StringRes int titleResId) {
- mLaunchRequest.titleResPackageName = titlePackageName;
- mLaunchRequest.titleResId = titleResId;
- mLaunchRequest.title = null;
+ mLaunchRequest.mTitleResPackageName = titlePackageName;
+ mLaunchRequest.mTitleResId = titleResId;
+ mLaunchRequest.mTitle = null;
return this;
}
@@ -82,22 +82,22 @@
* @param title text title
*/
public SubSettingLauncher setTitleText(CharSequence title) {
- mLaunchRequest.title = title;
+ mLaunchRequest.mTitle = title;
return this;
}
public SubSettingLauncher setArguments(Bundle arguments) {
- mLaunchRequest.arguments = arguments;
+ mLaunchRequest.mArguments = arguments;
return this;
}
public SubSettingLauncher setExtras(Bundle extras) {
- mLaunchRequest.extras = extras;
+ mLaunchRequest.mExtras = extras;
return this;
}
public SubSettingLauncher setSourceMetricsCategory(int sourceMetricsCategory) {
- mLaunchRequest.sourceMetricsCategory = sourceMetricsCategory;
+ mLaunchRequest.mSourceMetricsCategory = sourceMetricsCategory;
return this;
}
@@ -108,17 +108,23 @@
}
public SubSettingLauncher addFlags(int flags) {
- mLaunchRequest.flags |= flags;
+ mLaunchRequest.mFlags |= flags;
return this;
}
public SubSettingLauncher setUserHandle(UserHandle userHandle) {
- mLaunchRequest.userHandle = userHandle;
+ mLaunchRequest.mUserHandle = userHandle;
return this;
}
public SubSettingLauncher setTransitionType(int transitionType) {
- mLaunchRequest.transitionType = transitionType;
+ mLaunchRequest.mTransitionType = transitionType;
+ return this;
+ }
+
+ /** Decide whether the next page is secondary layer page or not. */
+ public SubSettingLauncher setIsSecondaryLayerPage(boolean isSecondaryLayerPage) {
+ mLaunchRequest.mIsSecondaryLayerPage = isSecondaryLayerPage;
return this;
}
@@ -131,14 +137,14 @@
final Intent intent = toIntent();
- boolean launchAsUser = mLaunchRequest.userHandle != null
- && mLaunchRequest.userHandle.getIdentifier() != UserHandle.myUserId();
+ boolean launchAsUser = mLaunchRequest.mUserHandle != null
+ && mLaunchRequest.mUserHandle.getIdentifier() != UserHandle.myUserId();
boolean launchForResult = mLaunchRequest.mResultListener != null;
if (launchAsUser && launchForResult) {
- launchForResultAsUser(intent, mLaunchRequest.userHandle, mLaunchRequest.mResultListener,
- mLaunchRequest.mRequestCode);
+ launchForResultAsUser(intent, mLaunchRequest.mUserHandle,
+ mLaunchRequest.mResultListener, mLaunchRequest.mRequestCode);
} else if (launchAsUser && !launchForResult) {
- launchAsUser(intent, mLaunchRequest.userHandle);
+ launchAsUser(intent, mLaunchRequest.mUserHandle);
} else if (!launchAsUser && launchForResult) {
launchForResult(mLaunchRequest.mResultListener, intent, mLaunchRequest.mRequestCode);
} else {
@@ -150,26 +156,28 @@
final Intent intent = new Intent(Intent.ACTION_MAIN);
copyExtras(intent);
intent.setClass(mContext, SubSettings.class);
- if (TextUtils.isEmpty(mLaunchRequest.destinationName)) {
+ if (TextUtils.isEmpty(mLaunchRequest.mDestinationName)) {
throw new IllegalArgumentException("Destination fragment must be set");
}
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, mLaunchRequest.destinationName);
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, mLaunchRequest.mDestinationName);
- if (mLaunchRequest.sourceMetricsCategory < 0) {
+ if (mLaunchRequest.mSourceMetricsCategory < 0) {
throw new IllegalArgumentException("Source metrics category must be set");
}
intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
- mLaunchRequest.sourceMetricsCategory);
+ mLaunchRequest.mSourceMetricsCategory);
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, mLaunchRequest.arguments);
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, mLaunchRequest.mArguments);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
- mLaunchRequest.titleResPackageName);
+ mLaunchRequest.mTitleResPackageName);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
- mLaunchRequest.titleResId);
- intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.title);
- intent.addFlags(mLaunchRequest.flags);
+ mLaunchRequest.mTitleResId);
+ intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.mTitle);
+ intent.addFlags(mLaunchRequest.mFlags);
intent.putExtra(SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
- mLaunchRequest.transitionType);
+ mLaunchRequest.mTransitionType);
+ intent.putExtra(SettingsActivity.EXTRA_IS_SECONDARY_LAYER_PAGE,
+ mLaunchRequest.mIsSecondaryLayerPage);
return intent;
}
@@ -196,8 +204,8 @@
}
private void copyExtras(Intent intent) {
- if (mLaunchRequest.extras != null) {
- intent.replaceExtras(mLaunchRequest.extras);
+ if (mLaunchRequest.mExtras != null) {
+ intent.replaceExtras(mLaunchRequest.mExtras);
}
}
@@ -205,17 +213,18 @@
* Simple container that has information about how to launch a subsetting.
*/
static class LaunchRequest {
- String destinationName;
- int titleResId;
- String titleResPackageName;
- CharSequence title;
- int sourceMetricsCategory = -100;
- int flags;
+ String mDestinationName;
+ int mTitleResId;
+ String mTitleResPackageName;
+ CharSequence mTitle;
+ int mSourceMetricsCategory = -100;
+ int mFlags;
Fragment mResultListener;
int mRequestCode;
- UserHandle userHandle;
- int transitionType;
- Bundle arguments;
- Bundle extras;
+ UserHandle mUserHandle;
+ int mTransitionType;
+ Bundle mArguments;
+ Bundle mExtras;
+ boolean mIsSecondaryLayerPage;
}
}
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index cfdfdaa..8b1d633 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -53,6 +53,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -280,6 +281,11 @@
return null;
}
+ /** Returns all controllers of type T. */
+ protected <T extends AbstractPreferenceController> List<T> useAll(Class<T> clazz) {
+ return (List<T>) mPreferenceControllers.getOrDefault(clazz, Collections.emptyList());
+ }
+
protected void addPreferenceController(AbstractPreferenceController controller) {
if (mPreferenceControllers.get(controller.getClass()) == null) {
mPreferenceControllers.put(controller.getClass(), new ArrayList<>());
diff --git a/src/com/android/settings/dashboard/profileselector/UserAdapter.java b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
index 7057961..abf22df 100644
--- a/src/com/android/settings/dashboard/profileselector/UserAdapter.java
+++ b/src/com/android/settings/dashboard/profileselector/UserAdapter.java
@@ -78,7 +78,7 @@
}
private static Drawable encircle(Context context, Drawable icon) {
- return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
+ return new UserIconDrawable(UserIconDrawable.getDefaultSize(context))
.setIconDrawable(icon).bake();
}
}
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
index 1292d00..2cabd8d 100644
--- a/src/com/android/settings/datausage/CycleAdapter.java
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -27,7 +27,7 @@
import com.android.settings.Utils;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.NetworkCycleData;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import java.time.ZonedDateTime;
import java.util.Iterator;
diff --git a/src/com/android/settings/datausage/SpinnerPreference.java b/src/com/android/settings/datausage/SpinnerPreference.java
index 67298a1..867930b 100644
--- a/src/com/android/settings/datausage/SpinnerPreference.java
+++ b/src/com/android/settings/datausage/SpinnerPreference.java
@@ -18,12 +18,12 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.Spinner;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
@@ -63,7 +63,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.cycles_spinner);
+ Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
index ceba8be..08e523c 100644
--- a/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
+++ b/src/com/android/settings/datausage/UnrestrictedDataAccessPreference.java
@@ -48,7 +48,6 @@
ApplicationsState applicationsState, DataSaverBackend dataSaverBackend,
DashboardFragment parentFragment) {
super(context);
- setWidgetLayoutResource(R.layout.restricted_switch_widget);
mHelper = new RestrictedPreferenceHelper(context, this, null);
mEntry = entry;
mDataUsageState = (AppStateDataUsageBridge.DataUsageState) mEntry.extraInfo;
@@ -131,10 +130,6 @@
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
- holder.findViewById(R.id.restricted_icon).setVisibility(
- disabledByAdmin ? View.VISIBLE : View.GONE);
- holder.findViewById(android.R.id.switch_widget).setVisibility(
- disabledByAdmin ? View.GONE : View.VISIBLE);
}
@Override
diff --git a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
index d6bdb77..992aa0f 100644
--- a/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
+++ b/src/com/android/settings/development/featureflags/FeatureFlagPreference.java
@@ -17,6 +17,8 @@
package com.android.settings.development.featureflags;
import android.content.Context;
+import android.provider.Settings;
+import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import androidx.preference.SwitchPreference;
@@ -49,5 +51,13 @@
} else {
FeatureFlagUtils.setEnabled(getContext(), mKey, isChecked);
}
+
+ // A temporary logic for settings_hide_secondary_page_back_button_in_two_pane
+ // Remove it before Android T release.
+ if (TextUtils.equals(mKey,
+ FeatureFlagUtils.SETTINGS_HIDE_SECONDARY_PAGE_BACK_BUTTON_IN_TWO_PANE)) {
+ Settings.Global.putString(getContext().getContentResolver(),
+ mKey, String.valueOf(isChecked));
+ }
}
}
diff --git a/src/com/android/settings/development/tare/AlarmManagerFragment.java b/src/com/android/settings/development/tare/AlarmManagerFragment.java
index dbc4e58..fe76b12 100644
--- a/src/com/android/settings/development/tare/AlarmManagerFragment.java
+++ b/src/com/android/settings/development/tare/AlarmManagerFragment.java
@@ -15,17 +15,16 @@
*/
package com.android.settings.development.tare;
+import android.annotation.Nullable;
import android.app.Fragment;
+import android.app.tare.EconomyManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
-import android.widget.TextView;
-import android.widget.Toast;
import com.android.settings.R;
@@ -33,130 +32,96 @@
* Creates the AlarmManager fragment to display all the AlarmManager factors
* when the AlarmManager policy is chosen in the dropdown TARE menu.
*/
-public class AlarmManagerFragment extends Fragment {
+public class AlarmManagerFragment extends Fragment implements
+ TareFactorController.DataChangeListener {
+
+ private TareFactorController mFactorController;
+
+ private TareFactorExpandableListAdapter mExpandableListAdapter;
+
+ private String[] mGroups;
+ private String[][] mChildren;
+ private String[][] mKeys;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mFactorController = TareFactorController.getInstance(getContext());
+ populateArrays();
+ }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tare_policy_fragment, null);
ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
- final SavedTabsListAdapter expListAdapter = new SavedTabsListAdapter();
+ mExpandableListAdapter = new TareFactorExpandableListAdapter(
+ mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
elv.setGroupIndicator(null);
- elv.setAdapter(expListAdapter);
+ elv.setAdapter(mExpandableListAdapter);
elv.setOnChildClickListener(new OnChildClickListener() {
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
- final String selected =
- (String) expListAdapter.getChild(groupPosition, childPosition);
- Toast.makeText(getActivity(), selected, Toast.LENGTH_SHORT).show();
+ final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
+ mFactorController.createDialog(key).show(getFragmentManager(), key);
return true;
}
});
return v;
}
- /**
- * Creates the expandable list containing all AlarmManager factors within the
- * AlarmManager fragment.
- */
- public class SavedTabsListAdapter extends BaseExpandableListAdapter {
+ @Override
+ public void onStart() {
+ super.onStart();
+ mFactorController.registerListener(this);
+ }
- private final LayoutInflater mInflater;
- private Resources mResources = getActivity().getResources();
+ @Override
+ public void onStop() {
+ mFactorController.unregisterListener(this);
+ super.onStop();
+ }
- private String[] mGroups = {
- mResources.getString(R.string.tare_max_circulation),
- mResources.getString(R.string.tare_max_satiated_balance),
- mResources.getString(R.string.tare_min_satiated_balance),
- mResources.getString(R.string.tare_modifiers),
- mResources.getString(R.string.tare_actions),
- mResources.getString(R.string.tare_rewards)
+ @Override
+ public void onDataChanged() {
+ mExpandableListAdapter.notifyDataSetChanged();
+ }
+
+ private void populateArrays() {
+ final Resources resources = getResources();
+
+ mGroups = new String[]{
+ resources.getString(R.string.tare_consumption_limits),
+ resources.getString(R.string.tare_balances),
+ // resources.getString(R.string.tare_modifiers),
+ // resources.getString(R.string.tare_actions),
+ // resources.getString(R.string.tare_rewards)
};
- /*
- * First two are empty arrays because the first two factors have no subfactors (no
- * children).
- */
- private String[][] mChildren = {
- {},
- {},
- mResources.getStringArray(R.array.tare_min_satiated_balance_subfactors),
- mResources.getStringArray(R.array.tare_modifiers_subfactors),
- mResources.getStringArray(R.array.tare_alarm_manager_actions),
- mResources.getStringArray(R.array.tare_rewards_subfactors)
+ mChildren = new String[][]{
+ resources.getStringArray(R.array.tare_consumption_limit_subfactors),
+ resources.getStringArray(R.array.tare_app_balance_subfactors),
+ // TODO: support
+ // resources.getStringArray(R.array.tare_modifiers_subfactors),
+ // resources.getStringArray(R.array.tare_alarm_manager_actions),
+ // resources.getStringArray(R.array.tare_rewards_subfactors)
};
- public SavedTabsListAdapter() {
- mInflater = LayoutInflater.from(getActivity());
- }
-
- @Override
- public int getGroupCount() {
- return mGroups.length;
- }
-
- @Override
- public int getChildrenCount(int groupPosition) {
- return mChildren[groupPosition].length;
- }
-
- @Override
- public Object getGroup(int groupPosition) {
- return mGroups[groupPosition];
- }
-
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return mChildren[groupPosition][childPosition];
- }
-
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
- }
- TextView factor = (TextView) convertView.findViewById(android.R.id.text1);
- factor.setText(getGroup(groupPosition).toString());
- return convertView;
- }
-
- @Override
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- // Here a custom child item is used instead of android.R.simple_list_item_2 because it
- // is more customizable for this specific UI
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.tare_child_item, null);
- }
- TextView factor = (TextView) convertView.findViewById(R.id.factor);
- TextView value = (TextView) convertView.findViewById(R.id.factor_number);
-
- // TODO: Replace these hardcoded values with either default or user inputted TARE values
- factor.setText(getChild(groupPosition, childPosition).toString());
- value.setText("500");
-
- return convertView;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
+ mKeys = new String[][]{
+ {
+ EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
+ EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT
+ },
+ {
+ EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
+ EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
+ EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
+ EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP
+ },
+ // {},
+ // {},
+ // {},
+ };
}
}
diff --git a/src/com/android/settings/development/tare/DropdownActivity.java b/src/com/android/settings/development/tare/DropdownActivity.java
index c1a11fa..66b57dd 100644
--- a/src/com/android/settings/development/tare/DropdownActivity.java
+++ b/src/com/android/settings/development/tare/DropdownActivity.java
@@ -26,6 +26,7 @@
import android.widget.Spinner;
import com.android.settings.R;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
/**
* Dropdown activity to allow for the user to easily switch between the different TARE
@@ -42,7 +43,6 @@
static final int POLICY_JOB_SCHEDULER = 1;
private static final int DEFAULT_POLICY = POLICY_ALARM_MANAGER;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -58,9 +58,8 @@
String[] policies = getResources().getStringArray(R.array.tare_policies);
- ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(DropdownActivity.this,
- android.R.layout.simple_list_item_1, policies);
- arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ ArrayAdapter<String> arrayAdapter = new SettingsSpinnerAdapter<String>(this);
+ arrayAdapter.addAll(policies);
mSpinner.setAdapter(arrayAdapter);
mSpinner.setSelection(policy);
diff --git a/src/com/android/settings/development/tare/JobSchedulerFragment.java b/src/com/android/settings/development/tare/JobSchedulerFragment.java
index 5a7f4a9..1c6598c 100644
--- a/src/com/android/settings/development/tare/JobSchedulerFragment.java
+++ b/src/com/android/settings/development/tare/JobSchedulerFragment.java
@@ -15,17 +15,16 @@
*/
package com.android.settings.development.tare;
+import android.annotation.Nullable;
import android.app.Fragment;
+import android.app.tare.EconomyManager;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
-import android.widget.TextView;
-import android.widget.Toast;
import com.android.settings.R;
@@ -33,131 +32,97 @@
* Creates the JobScheduler fragment to display all the JobScheduler factors
* when the JobScheduler policy is chosen in the dropdown TARE menu.
*/
-public class JobSchedulerFragment extends Fragment {
+public class JobSchedulerFragment extends Fragment implements
+ TareFactorController.DataChangeListener {
+
+ private TareFactorController mFactorController;
+
+ private TareFactorExpandableListAdapter mExpandableListAdapter;
+
+ private String[] mGroups;
+ private String[][] mChildren;
+ private String[][] mKeys;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mFactorController = TareFactorController.getInstance(getContext());
+ populateArrays();
+ }
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.tare_policy_fragment, null);
ExpandableListView elv = (ExpandableListView) v.findViewById(R.id.factor_list);
- final SavedTabsListAdapter expListAdapter = new SavedTabsListAdapter();
+ mExpandableListAdapter = new TareFactorExpandableListAdapter(
+ mFactorController, LayoutInflater.from(getActivity()), mGroups, mChildren, mKeys);
elv.setGroupIndicator(null);
- elv.setAdapter(expListAdapter);
+ elv.setAdapter(mExpandableListAdapter);
elv.setOnChildClickListener(new OnChildClickListener() {
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
- final String selected =
- (String) expListAdapter.getChild(groupPosition, childPosition);
- Toast.makeText(getActivity(), selected, Toast.LENGTH_SHORT)
- .show();
+ final String key = mExpandableListAdapter.getKey(groupPosition, childPosition);
+ mFactorController.createDialog(key).show(getFragmentManager(), key);
return true;
}
});
+
return v;
}
- /**
- * Creates the expandable list containing all JobScheduler factors within the
- * JobScheduler fragment.
- */
- public class SavedTabsListAdapter extends BaseExpandableListAdapter {
+ @Override
+ public void onStart() {
+ super.onStart();
+ mFactorController.registerListener(this);
+ }
- private final LayoutInflater mInflater;
- private Resources mResources = getActivity().getResources();
+ @Override
+ public void onStop() {
+ mFactorController.unregisterListener(this);
+ super.onStop();
+ }
- private String[] mGroups = {
- mResources.getString(R.string.tare_max_circulation),
- mResources.getString(R.string.tare_max_satiated_balance),
- mResources.getString(R.string.tare_min_satiated_balance),
- mResources.getString(R.string.tare_modifiers),
- mResources.getString(R.string.tare_actions),
- mResources.getString(R.string.tare_rewards)
+ @Override
+ public void onDataChanged() {
+ mExpandableListAdapter.notifyDataSetChanged();
+ }
+
+ private void populateArrays() {
+ final Resources resources = getResources();
+
+ mGroups = new String[]{
+ resources.getString(R.string.tare_consumption_limits),
+ resources.getString(R.string.tare_balances),
+ // mResources.getString(R.string.tare_modifiers),
+ // mResources.getString(R.string.tare_actions),
+ // mResources.getString(R.string.tare_rewards)
};
- /*
- * First two are empty arrays because the first two factors have no subfactors (no
- * children).
- */
- private String[][] mChildren = {
- {},
- {},
- mResources.getStringArray(R.array.tare_min_satiated_balance_subfactors),
- mResources.getStringArray(R.array.tare_modifiers_subfactors),
- mResources.getStringArray(R.array.tare_job_scheduler_actions),
- mResources.getStringArray(R.array.tare_rewards_subfactors)
+ mChildren = new String[][]{
+ resources.getStringArray(R.array.tare_consumption_limit_subfactors),
+ resources.getStringArray(R.array.tare_app_balance_subfactors),
+ // TODO: support
+ // mResources.getStringArray(R.array.tare_modifiers_subfactors),
+ // mResources.getStringArray(R.array.tare_job_scheduler_actions),
+ // mResources.getStringArray(R.array.tare_rewards_subfactors)
};
- public SavedTabsListAdapter() {
- mInflater = LayoutInflater.from(getActivity());
- }
-
- @Override
- public int getGroupCount() {
- return mGroups.length;
- }
-
- @Override
- public int getChildrenCount(int groupPosition) {
- return mChildren[groupPosition].length;
- }
-
- @Override
- public Object getGroup(int groupPosition) {
- return mGroups[groupPosition];
- }
-
- @Override
- public Object getChild(int groupPosition, int childPosition) {
- return mChildren[groupPosition][childPosition];
- }
-
- @Override
- public long getGroupId(int groupPosition) {
- return groupPosition;
- }
-
- @Override
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(android.R.layout.simple_list_item_1, parent, false);
- }
- TextView factor = (TextView) convertView.findViewById(android.R.id.text1);
- factor.setText(getGroup(groupPosition).toString());
- return convertView;
- }
-
- @Override
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- // Here a custom child item is used instead of android.R.simple_list_item_2 because it
- // is more customizable for this specific UI
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.tare_child_item, null);
- }
- TextView factor = (TextView) convertView.findViewById(R.id.factor);
- TextView value = (TextView) convertView.findViewById(R.id.factor_number);
-
- // TODO: Replace these hardcoded values with either default or user inputted TARE values
- factor.setText(getChild(groupPosition, childPosition).toString());
- value.setText("500");
-
- return convertView;
- }
-
- @Override
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
+ mKeys = new String[][]{
+ {
+ EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
+ EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT
+ },
+ {
+ EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
+ EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
+ EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
+ EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP
+ },
+ // {},
+ // {},
+ // {},
+ };
}
}
diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java
index 50531a0..b9f813d 100644
--- a/src/com/android/settings/development/tare/TareFactorController.java
+++ b/src/com/android/settings/development/tare/TareFactorController.java
@@ -16,12 +16,21 @@
package com.android.settings.development.tare;
+import static android.provider.Settings.Global.TARE_ALARM_MANAGER_CONSTANTS;
+import static android.provider.Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS;
+
+import android.annotation.NonNull;
import android.app.tare.EconomyManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -34,6 +43,8 @@
public class TareFactorController {
private static final String TAG = "TareFactorController";
+ private static TareFactorController sInstance;
+
private static final int POLICY_ALARM_MANAGER = 0;
private static final int POLICY_JOB_SCHEDULER = 1;
@@ -45,15 +56,19 @@
private String mAlarmManagerConstants;
private String mJobSchedulerConstants;
- public TareFactorController(Context context) {
+ private final ArraySet<DataChangeListener> mDataChangeListeners = new ArraySet<>();
+
+ private TareFactorController(Context context) {
mContentResolver = context.getContentResolver();
mResources = context.getResources();
- mAlarmManagerConstants = Settings.Global
- .getString(mContentResolver, Settings.Global.TARE_ALARM_MANAGER_CONSTANTS);
+ ConfigObserver configObserver = new ConfigObserver(new Handler(Looper.getMainLooper()));
+ configObserver.start();
- mJobSchedulerConstants = Settings.Global
- .getString(mContentResolver, Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS);
+ mAlarmManagerConstants =
+ Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
+ mJobSchedulerConstants =
+ Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
initAlarmManagerMap();
parseAlarmManagerGlobalSettings();
@@ -62,30 +77,43 @@
parseJobSchedulerGlobalSettings();
}
+ static TareFactorController getInstance(Context context) {
+ synchronized (TareFactorController.class) {
+ if (sInstance == null) {
+ sInstance = new TareFactorController(context.getApplicationContext());
+ }
+ }
+ return sInstance;
+ }
+
/**
* Initialization for AlarmManager Map that sets a AM factor key to a title, default value, and
* policy type in a data object.
*/
private void initAlarmManagerMap() {
mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_EXEMPTED,
- new TareFactorData(mResources.getString(R.string.tare_min_satiated_balance),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_EXEMPTED,
POLICY_ALARM_MANAGER));
mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- new TareFactorData(mResources.getString(R.string.tare_headless_app),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
POLICY_ALARM_MANAGER));
mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_SATIATED_BALANCE_OTHER_APP,
- new TareFactorData(mResources.getString(R.string.tare_other_app),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
EconomyManager.DEFAULT_AM_MIN_SATIATED_BALANCE_OTHER_APP,
POLICY_ALARM_MANAGER));
mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
EconomyManager.DEFAULT_AM_MAX_SATIATED_BALANCE,
POLICY_ALARM_MANAGER));
- mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_CIRCULATION,
- new TareFactorData(mResources.getString(R.string.tare_max_circulation),
- EconomyManager.DEFAULT_AM_MAX_CIRCULATION,
+ mAlarmManagerMap.put(EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
+ new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
+ EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT,
+ POLICY_ALARM_MANAGER));
+ mAlarmManagerMap.put(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT,
+ new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
+ EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT,
POLICY_ALARM_MANAGER));
mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
new TareFactorData(mResources.getString(R.string.tare_top_activity),
@@ -250,24 +278,28 @@
*/
private void initJobSchedulerMap() {
mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED,
- new TareFactorData(mResources.getString(R.string.tare_min_satiated_balance),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_exempted),
EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
- new TareFactorData(mResources.getString(R.string.tare_headless_app),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_headless_app),
EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_HEADLESS_SYSTEM_APP,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP,
- new TareFactorData(mResources.getString(R.string.tare_other_app),
+ new TareFactorData(mResources.getString(R.string.tare_min_balance_other_app),
EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
new TareFactorData(mResources.getString(R.string.tare_max_satiated_balance),
EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE,
POLICY_JOB_SCHEDULER));
- mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_CIRCULATION,
- new TareFactorData(mResources.getString(R.string.tare_max_circulation),
- EconomyManager.DEFAULT_JS_MAX_CIRCULATION,
+ mJobSchedulerMap.put(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
+ new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
+ EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT,
+ POLICY_JOB_SCHEDULER));
+ mJobSchedulerMap.put(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT,
+ new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
+ EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_TOP_ACTIVITY_INSTANT,
new TareFactorData(mResources.getString(R.string.tare_top_activity),
@@ -408,8 +440,7 @@
mJobSchedulerMap.put(
EconomyManager.KEY_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
new TareFactorData(mResources.getString(R.string.tare_job_low_running),
- EconomyManager
- .DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
+ EconomyManager.DEFAULT_JS_ACTION_JOB_LOW_RUNNING_BASE_PRICE,
POLICY_JOB_SCHEDULER));
mJobSchedulerMap.put(EconomyManager.KEY_JS_ACTION_JOB_MIN_START_BASE_PRICE,
new TareFactorData(mResources.getString(R.string.tare_job_min_start),
@@ -425,103 +456,65 @@
POLICY_JOB_SCHEDULER));
}
-
- /**
- * Takes a key and factor policy as input and grabs the default value linked to it.
- *
- * @param key the key of the factor you want to get the default value of
- * @param factorPolicy the policy you want the default value of
- */
- private int getDefaultValue(String key, int factorPolicy) {
- ArrayMap<String, TareFactorData> currentMap;
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- currentMap = mAlarmManagerMap;
- break;
- case POLICY_JOB_SCHEDULER:
- currentMap = mJobSchedulerMap;
- break;
- default:
- throw new IllegalArgumentException("Invalid factor policy given");
- }
- return currentMap.get(key).defaultValue;
- }
-
/**
* Parses the AM constant from Settings.Global to get to the current value.
*/
private void parseAlarmManagerGlobalSettings() {
- try {
- mParser.setString(mAlarmManagerConstants);
- } catch (Exception e) {
- Slog.e(TAG, "Bad value string constants", e);
- }
- int size = mParser.size();
-
- for (int i = 0; i < size - 1; i++) {
- String key = mParser.keyAt(i);
- TareFactorData data = mAlarmManagerMap.get(key);
- data.currentValue = mParser.getInt(key, getDefaultValue(key, getFactorType(key)));
- }
+ parseSettingsIntoMap(mAlarmManagerConstants, mAlarmManagerMap);
}
/**
* Parses the JS constant from Settings.Global to get to the current value.
*/
private void parseJobSchedulerGlobalSettings() {
- try {
- mParser.setString(mJobSchedulerConstants);
- } catch (Exception e) {
- Slog.e(TAG, "Bad value string constants", e);
- }
- int size = mParser.size();
+ parseSettingsIntoMap(mJobSchedulerConstants, mJobSchedulerMap);
+ }
- for (int i = 0; i < size - 1; i++) {
- String key = mParser.keyAt(i);
- TareFactorData data = mJobSchedulerMap.get(key);
- data.currentValue = mParser.getInt(key, getDefaultValue(key, getFactorType(key)));
+ private void parseSettingsIntoMap(String constants, ArrayMap<String, TareFactorData> map) {
+ try {
+ mParser.setString(constants);
+ } catch (Exception e) {
+ Slog.e(TAG, "Bad string constants value", e);
+ }
+
+ for (int i = map.size() - 1; i >= 0; --i) {
+ final String key = map.keyAt(i);
+ final TareFactorData data = map.valueAt(i);
+ data.currentValue = mParser.getInt(key, data.defaultValue);
+ }
+ }
+
+ @NonNull
+ private ArrayMap<String, TareFactorData> getMap(int factorPolicy) {
+ switch (factorPolicy) {
+ case POLICY_ALARM_MANAGER:
+ return mAlarmManagerMap;
+ case POLICY_JOB_SCHEDULER:
+ return mJobSchedulerMap;
+ default:
+ throw new IllegalArgumentException("Invalid factor policy given");
}
}
/**
* Takes a key and factor policy as input and grabs the title linked to it.
*
- * @param key the key of the factor you want to get the title of
+ * @param key the key of the factor you want to get the title of
* @param factorPolicy the policy you want the title of
*/
private String getTitle(String key, int factorPolicy) {
- ArrayMap<String, TareFactorData> currentMap;
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- currentMap = mAlarmManagerMap;
- break;
- case POLICY_JOB_SCHEDULER:
- currentMap = mJobSchedulerMap;
- break;
- default:
- throw new IllegalArgumentException("Invalid factor policy given");
- }
+ final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
return currentMap.get(key).title;
}
/**
* Takes a key and factor policy as input and grabs the current value linked to it.
*
- * @param key the key of the factor you want to get the default value of
+ * @param key the key of the factor you want to get the default value of
* @param factorPolicy the policy you want the current value of
*/
private int getCurrentValue(String key, int factorPolicy) {
- ArrayMap<String, TareFactorData> currentMap;
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- currentMap = mAlarmManagerMap;
- break;
- case POLICY_JOB_SCHEDULER:
- currentMap = mJobSchedulerMap;
- break;
- default:
- throw new IllegalArgumentException("Invalid factor policy given");
- }
+ final ArrayMap<String, TareFactorData> currentMap = getMap(factorPolicy);
return currentMap.get(key).currentValue;
}
@@ -542,6 +535,11 @@
return currentMap.get(key).factorPolicy;
}
+ int getValue(String key) {
+ final int policy = getFactorType(key);
+ return getCurrentValue(key, policy);
+ }
+
/**
* Takes a key,edited value, and factor policy as input and assigns the new edited value to
* be the new current value for that factors key.
@@ -551,20 +549,15 @@
* @param factorPolicy policy being updated
*/
public void updateValue(String key, int editedValue, int factorPolicy) {
- switch (factorPolicy) {
- case POLICY_ALARM_MANAGER:
- mAlarmManagerMap.get(key).currentValue = editedValue;
- rebuildPolicyConstants(factorPolicy);
- break;
- case POLICY_JOB_SCHEDULER:
- mJobSchedulerMap.get(key).currentValue = editedValue;
- rebuildPolicyConstants(factorPolicy);
- break;
- default:
- throw new IllegalArgumentException("Invalid factor policy given");
- }
- }
+ final ArrayMap<String, TareFactorData> map = getMap(factorPolicy);
+ final TareFactorData data = map.get(key);
+ if (data.currentValue == editedValue) {
+ return;
+ }
+ data.currentValue = editedValue;
+ rebuildPolicyConstants(factorPolicy);
+ }
/**
* Iterates through the factor policy map for keys and current values to
@@ -573,58 +566,39 @@
* @param factorPolicy policy being updated
*/
private void rebuildPolicyConstants(int factorPolicy) {
- StringBuilder newConstantsStringBuilder = new StringBuilder();
-
switch (factorPolicy) {
case POLICY_ALARM_MANAGER:
- int sizeAM = mAlarmManagerMap.size();
-
- for (int i = 0; i < sizeAM; i++) {
- if (i > 0) {
- newConstantsStringBuilder.append(",");
- }
-
- String key = mAlarmManagerMap.keyAt(i);
- newConstantsStringBuilder.append(key + "=" + mAlarmManagerMap.get(key)
- .currentValue);
- }
-
- String newAMConstantsString = newConstantsStringBuilder.toString();
-
- Settings.Global.putString(mContentResolver, Settings.Global
- .TARE_ALARM_MANAGER_CONSTANTS,
- newAMConstantsString);
-
- mAlarmManagerConstants = Settings.Global
- .getString(mContentResolver, Settings.Global
- .TARE_ALARM_MANAGER_CONSTANTS);
+ writeConstantsToSettings(mAlarmManagerMap, TARE_ALARM_MANAGER_CONSTANTS);
break;
case POLICY_JOB_SCHEDULER:
- int sizeJS = mJobSchedulerMap.size();
-
- for (int i = 0; i < sizeJS; i++) {
- if (i > 0) {
- newConstantsStringBuilder.append(",");
- }
-
- String key = mJobSchedulerMap.keyAt(i);
- newConstantsStringBuilder.append(key + "=" + mJobSchedulerMap.get(key)
- .currentValue);
- }
-
- String newJSConstantsString = newConstantsStringBuilder.toString();
-
- Settings.Global.putString(mContentResolver, Settings.Global
- .TARE_JOB_SCHEDULER_CONSTANTS,
- newJSConstantsString);
-
- mJobSchedulerConstants = Settings.Global
- .getString(mContentResolver, Settings.Global
- .TARE_JOB_SCHEDULER_CONSTANTS);
+ writeConstantsToSettings(mJobSchedulerMap, TARE_JOB_SCHEDULER_CONSTANTS);
break;
}
}
+ private void writeConstantsToSettings(ArrayMap<String, TareFactorData> factorMap,
+ String settingsKey) {
+ final StringBuilder constantsStringBuilder = new StringBuilder();
+
+ for (int i = 0, size = factorMap.size(); i < size; ++i) {
+ final TareFactorData factor = factorMap.valueAt(i);
+ if (factor.currentValue == factor.defaultValue) {
+ continue;
+ }
+
+ if (constantsStringBuilder.length() > 0) {
+ constantsStringBuilder.append(",");
+ }
+
+ constantsStringBuilder
+ .append(factorMap.keyAt(i))
+ .append("=")
+ .append(factor.currentValue);
+ }
+
+ Settings.Global.putString(mContentResolver, settingsKey, constantsStringBuilder.toString());
+ }
+
/**
* Creates a dialog with the values linked to the key.
*
@@ -633,7 +607,7 @@
public TareFactorDialogFragment createDialog(String key) {
int policy = getFactorType(key);
return new TareFactorDialogFragment(getTitle(key, policy), key,
- getCurrentValue(key, policy), policy , this);
+ getCurrentValue(key, policy), policy, this);
}
/**
@@ -652,4 +626,51 @@
this.currentValue = defaultValue;
}
}
-}
\ No newline at end of file
+
+ interface DataChangeListener {
+ void onDataChanged();
+ }
+
+ void registerListener(DataChangeListener listener) {
+ mDataChangeListeners.add(listener);
+ }
+
+ void unregisterListener(DataChangeListener listener) {
+ mDataChangeListeners.remove(listener);
+ }
+
+ void notifyListeners() {
+ for (int i = mDataChangeListeners.size() - 1; i >= 0; --i) {
+ mDataChangeListeners.valueAt(i).onDataChanged();
+ }
+ }
+
+ private class ConfigObserver extends ContentObserver {
+
+ ConfigObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void start() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this);
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS))) {
+ mAlarmManagerConstants =
+ Settings.Global.getString(mContentResolver, TARE_ALARM_MANAGER_CONSTANTS);
+ parseAlarmManagerGlobalSettings();
+ notifyListeners();
+ } else if (uri.equals(Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS))) {
+ mJobSchedulerConstants =
+ Settings.Global.getString(mContentResolver, TARE_JOB_SCHEDULER_CONSTANTS);
+ parseJobSchedulerGlobalSettings();
+ notifyListeners();
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/development/tare/TareFactorDialogFragment.java b/src/com/android/settings/development/tare/TareFactorDialogFragment.java
index ff7f5f9..8d2f341 100644
--- a/src/com/android/settings/development/tare/TareFactorDialogFragment.java
+++ b/src/com/android/settings/development/tare/TareFactorDialogFragment.java
@@ -16,7 +16,10 @@
package com.android.settings.development.tare;
+import android.annotation.NonNull;
+import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.DialogFragment;
import android.content.Context;
import android.os.Bundle;
import android.text.InputType;
@@ -25,10 +28,6 @@
import android.view.View;
import android.widget.EditText;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.DialogFragment;
-
import com.android.settings.R;
import com.android.settings.Utils;
diff --git a/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java b/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
new file mode 100644
index 0000000..8fe4c05
--- /dev/null
+++ b/src/com/android/settings/development/tare/TareFactorExpandableListAdapter.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 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.development.tare;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.TextView;
+
+import com.android.settings.R;
+
+/**
+ * Creates the expandable list that will allow modifying individual factors.
+ */
+public class TareFactorExpandableListAdapter extends BaseExpandableListAdapter {
+
+ private final LayoutInflater mLayoutInflater;
+ private final TareFactorController mFactorController;
+
+ private final String[] mGroups;
+ private final String[][] mChildren;
+ private final String[][] mKeys;
+
+ TareFactorExpandableListAdapter(TareFactorController factorController,
+ LayoutInflater layoutInflater, String[] groups, String[][] children, String[][] keys) {
+ mLayoutInflater = layoutInflater;
+ mFactorController = factorController;
+
+ mGroups = groups;
+ mChildren = children;
+ mKeys = keys;
+
+ validateMappings();
+ }
+
+ private void validateMappings() {
+ if (mGroups.length != mChildren.length) {
+ throw new IllegalStateException("groups and children don't have the same length");
+ }
+ if (mChildren.length != mKeys.length) {
+ throw new IllegalStateException("children and keys don't have the same length");
+ }
+ for (int i = 0; i < mChildren.length; ++i) {
+ if (mChildren[i].length != mKeys[i].length) {
+ throw new IllegalStateException(
+ "children and keys don't have the same length in row " + i);
+ }
+ }
+ }
+
+ @Override
+ public int getGroupCount() {
+ return mGroups.length;
+ }
+
+ @Override
+ public int getChildrenCount(int groupPosition) {
+ return mChildren[groupPosition].length;
+ }
+
+ @Override
+ public Object getGroup(int groupPosition) {
+ return mGroups[groupPosition];
+ }
+
+ @Override
+ public Object getChild(int groupPosition, int childPosition) {
+ return mChildren[groupPosition][childPosition];
+ }
+
+ @Override
+ public long getGroupId(int groupPosition) {
+ return groupPosition;
+ }
+
+ @Override
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ @NonNull
+ String getKey(int groupPosition, int childPosition) {
+ return mKeys[groupPosition][childPosition];
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mLayoutInflater.inflate(android.R.layout.simple_list_item_1, parent,
+ false);
+ }
+ TextView factor = convertView.findViewById(android.R.id.text1);
+ factor.setText(getGroup(groupPosition).toString());
+ return convertView;
+ }
+
+ @Override
+ @SuppressLint("InflateParams") // AdapterView doesn't support addView
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ // Here a custom child item is used instead of android.R.simple_list_item_2 because it
+ // is more customizable for this specific UI
+ if (convertView == null) {
+ convertView = mLayoutInflater.inflate(R.layout.tare_child_item, null);
+ }
+ TextView factor = convertView.findViewById(R.id.factor);
+ TextView value = convertView.findViewById(R.id.factor_number);
+
+ factor.setText(getChild(groupPosition, childPosition).toString());
+ value.setText(String.valueOf(
+ mFactorController.getValue(getKey(groupPosition, childPosition))));
+
+ return convertView;
+ }
+
+ @Override
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+}
diff --git a/src/com/android/settings/development/tare/TareHomePage.java b/src/com/android/settings/development/tare/TareHomePage.java
index 38e7ed8..cea0954 100644
--- a/src/com/android/settings/development/tare/TareHomePage.java
+++ b/src/com/android/settings/development/tare/TareHomePage.java
@@ -69,11 +69,14 @@
}
/** Reverts the TARE settings to the original default settings */
- // TODO: Establish default TARE values and make this method revert all settings back to default.
public void revertSettings(View v) {
Toast.makeText(this, R.string.tare_settings_reverted_toast, Toast.LENGTH_LONG).show();
Settings.Global.putString(getApplicationContext().getContentResolver(),
Settings.Global.ENABLE_TARE, null);
+ Settings.Global.putString(getApplicationContext().getContentResolver(),
+ Settings.Global.TARE_ALARM_MANAGER_CONSTANTS, null);
+ Settings.Global.putString(getApplicationContext().getContentResolver(),
+ Settings.Global.TARE_JOB_SCHEDULER_CONSTANTS, null);
setEnabled(Settings.Global.DEFAULT_ENABLE_TARE == SETTING_VALUE_ON);
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 746d234..cf31724 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -47,6 +47,7 @@
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.StorageCacheHelper;
import com.android.settings.deviceinfo.storage.StorageEntry;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController;
@@ -109,6 +110,8 @@
private boolean mIsWorkProfile;
private int mUserId;
private Preference mFreeUpSpacePreference;
+ private boolean mIsLoadedFromCache;
+ private StorageCacheHelper mStorageCacheHelper;
private final StorageEventListener mStorageEventListener = new StorageEventListener() {
@Override
@@ -239,15 +242,27 @@
mPreferenceController.setVolume(null);
return;
}
+
+ if (mStorageCacheHelper.hasCachedSizeInfo() && mSelectedStorageEntry.isPrivate()) {
+ StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
+ mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
+ mPreferenceController.setUsedSize(cachedData.usedSize);
+ mPreferenceController.setTotalSize(cachedData.totalSize);
+ }
+
if (mSelectedStorageEntry.isPrivate()) {
mStorageInfo = null;
mAppsResult = null;
- maybeSetLoading(isQuotaSupported());
-
- // To prevent flicker, sets null volume to hide category preferences.
- // onReceivedSizes will setVolume with the volume of selected storage.
- mPreferenceController.setVolume(null);
-
+ // Hide the loading spinner if there is cached data.
+ if (mStorageCacheHelper.hasCachedSizeInfo()) {
+ //TODO(b/220259287): apply cache mechanism to secondary user
+ mPreferenceController.onLoadFinished(mAppsResult, mUserId);
+ } else {
+ maybeSetLoading(isQuotaSupported());
+ // To prevent flicker, sets null volume to hide category preferences.
+ // onReceivedSizes will setVolume with the volume of selected storage.
+ mPreferenceController.setVolume(null);
+ }
// Stats data is only available on private volumes.
getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
getLoaderManager()
@@ -277,6 +292,16 @@
initializePreference();
initializeOptionsMenu(activity);
+
+ if (mStorageCacheHelper.hasCachedSizeInfo()) {
+ mIsLoadedFromCache = true;
+ mStorageEntries.clear();
+ mStorageEntries.addAll(
+ StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
+ refreshUi();
+ updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
+ setSecondaryUsersVisible(true);
+ }
}
private void initializePreference() {
@@ -291,6 +316,7 @@
mUserManager = context.getSystemService(UserManager.class);
mIsWorkProfile = false;
mUserId = UserHandle.myUserId();
+ mStorageCacheHelper = new StorageCacheHelper(getContext(), mUserId);
super.onAttach(context);
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
@@ -323,9 +349,14 @@
public void onResume() {
super.onResume();
- mStorageEntries.clear();
- mStorageEntries.addAll(StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
- refreshUi();
+ if (mIsLoadedFromCache) {
+ mIsLoadedFromCache = false;
+ } else {
+ mStorageEntries.clear();
+ mStorageEntries.addAll(
+ StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
+ refreshUi();
+ }
mStorageManager.registerListener(mStorageEventListener);
}
@@ -333,6 +364,11 @@
public void onPause() {
super.onPause();
mStorageManager.unregisterListener(mStorageEventListener);
+ // Destroy the data loaders to prevent unnecessary data loading when switching back to the
+ // page.
+ getLoaderManager().destroyLoader(STORAGE_JOB_ID);
+ getLoaderManager().destroyLoader(ICON_JOB_ID);
+ getLoaderManager().destroyLoader(VOLUME_SIZE_JOB_ID);
}
@Override
@@ -359,6 +395,8 @@
mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
+ // Cache total size and used size
+ mStorageCacheHelper.cacheTotalSizeAndUsedSize(mStorageInfo.totalBytes, privateUsedBytes);
for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
final AbstractPreferenceController controller = mSecondaryUsers.get(i);
if (controller instanceof SecondaryUserController) {
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 934ff3f..d3549d4 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -66,7 +66,7 @@
return;
mProgressBar.setMax(PROGRESS_MAX);
- mProgressBar.setProgress(mProgressPercent);
+ mProgressBar.setProgress(mProgressPercent, true /* animate */);
}
@Override
diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
index f87a06a..84cafd4 100644
--- a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
+++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
@@ -183,6 +183,9 @@
@Override
public void handleResult(SparseArray<StorageAsyncLoader.StorageResult> stats) {
+ if (stats == null) {
+ return;
+ }
final StorageAsyncLoader.StorageResult result = stats.get(getUser().id);
if (result != null) {
setSize(result.externalStats.totalBytes);
diff --git a/src/com/android/settings/deviceinfo/storage/StorageCacheHelper.java b/src/com/android/settings/deviceinfo/storage/StorageCacheHelper.java
new file mode 100644
index 0000000..a868c9f
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageCacheHelper.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 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.content.SharedPreferences;
+
+/**
+ * A utility class to cache and restore the storage size information.
+ */
+public class StorageCacheHelper {
+
+ private static final String SHARED_PREFERENCE_NAME = "StorageCache";
+ private static final String TOTAL_SIZE_KEY = "total_size_key";
+ private static final String USED_SIZE_KEY = "used_size_key";
+ private static final String IMAGES_SIZE_KEY = "images_size_key";
+ private static final String VIDEOS_SIZE_KEY = "videos_size_key";
+ private static final String AUDIO_SIZE_KEY = "audio_size_key";
+ private static final String APPS_SIZE_KEY = "apps_size_key";
+ private static final String GAMES_SIZE_KEY = "games_size_key";
+ private static final String DOCUMENTS_AND_OTHER_SIZE_KEY = "documents_and_other_size_key";
+ private static final String TRASH_SIZE_KEY = "trash_size_key";
+ private static final String SYSTEM_SIZE_KEY = "system_size_key";
+
+ private final SharedPreferences mSharedPreferences;
+
+ public StorageCacheHelper(Context context, int userId) {
+ String sharedPrefName = SHARED_PREFERENCE_NAME + userId;
+ mSharedPreferences = context.getSharedPreferences(sharedPrefName, Context.MODE_PRIVATE);
+ }
+
+ /**
+ * Returns true if there's a cached size info.
+ */
+ public boolean hasCachedSizeInfo() {
+ return mSharedPreferences.getAll().size() > 0;
+ }
+
+ /**
+ * Cache the size info
+ * @param data a data about the file size info.
+ */
+ public void cacheSizeInfo(StorageCache data) {
+ mSharedPreferences
+ .edit()
+ .putLong(IMAGES_SIZE_KEY, data.imagesSize)
+ .putLong(VIDEOS_SIZE_KEY, data.videosSize)
+ .putLong(AUDIO_SIZE_KEY, data.audioSize)
+ .putLong(APPS_SIZE_KEY, data.allAppsExceptGamesSize)
+ .putLong(GAMES_SIZE_KEY, data.gamesSize)
+ .putLong(DOCUMENTS_AND_OTHER_SIZE_KEY, data.documentsAndOtherSize)
+ .putLong(TRASH_SIZE_KEY, data.trashSize)
+ .putLong(SYSTEM_SIZE_KEY, data.systemSize)
+ .apply();
+ }
+
+ /**
+ * Cache total size and used size
+ */
+ public void cacheTotalSizeAndUsedSize(long totalSize, long usedSize) {
+ mSharedPreferences
+ .edit()
+ .putLong(TOTAL_SIZE_KEY, totalSize)
+ .putLong(USED_SIZE_KEY, usedSize)
+ .apply();
+ }
+
+ /**
+ * Returns a cached data about all file size information.
+ */
+ public StorageCache retrieveCachedSize() {
+ StorageCache result = new StorageCache();
+ result.totalSize = mSharedPreferences.getLong(TOTAL_SIZE_KEY, 0);
+ result.usedSize = mSharedPreferences.getLong(USED_SIZE_KEY, 0);
+ result.imagesSize = mSharedPreferences.getLong(IMAGES_SIZE_KEY, 0);
+ result.videosSize = mSharedPreferences.getLong(VIDEOS_SIZE_KEY, 0);
+ result.audioSize = mSharedPreferences.getLong(AUDIO_SIZE_KEY, 0);
+ result.allAppsExceptGamesSize = mSharedPreferences.getLong(APPS_SIZE_KEY, 0);
+ result.gamesSize = mSharedPreferences.getLong(GAMES_SIZE_KEY, 0);
+ result.documentsAndOtherSize = mSharedPreferences.getLong(DOCUMENTS_AND_OTHER_SIZE_KEY, 0);
+ result.trashSize = mSharedPreferences.getLong(TRASH_SIZE_KEY, 0);
+ result.systemSize = mSharedPreferences.getLong(SYSTEM_SIZE_KEY, 0);
+ return result;
+ }
+
+ /**
+ * All the cached data about the file size information.
+ */
+ public static class StorageCache {
+ public long totalSize;
+ public long usedSize;
+ public long gamesSize;
+ public long allAppsExceptGamesSize;
+ public long audioSize;
+ public long imagesSize;
+ public long videosSize;
+ public long documentsAndOtherSize;
+ public long trashSize;
+ public long systemSize;
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index ee0c9e7..9813439 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -34,6 +34,7 @@
import android.util.SparseArray;
import android.widget.Toast;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
@@ -135,7 +136,11 @@
private boolean mIsWorkProfile;
- private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
+ private StorageCacheHelper mStorageCacheHelper;
+ // The mIsDocumentsPrefShown being used here is to prevent a flicker problem from displaying
+ // the Document entry.
+ private boolean mIsDocumentsPrefShown;
+ private boolean mIsPreferenceOrderedBySize;
public StorageItemPreferenceController(Context context, Fragment hostFragment,
VolumeInfo volume, StorageVolumeProvider svp, boolean isWorkProfile) {
@@ -148,6 +153,8 @@
mIsWorkProfile = isWorkProfile;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mUserId = getCurrentUserId();
+ mIsDocumentsPrefShown = isDocumentsPrefShown();
+ mStorageCacheHelper = new StorageCacheHelper(mContext, mUserId);
mImagesUri = Uri.parse(context.getResources()
.getString(R.string.config_images_storage_category_uri));
@@ -267,14 +274,17 @@
// 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.
if (visible) {
- final VolumeInfo sharedVolume = mSvp.findEmulatedForPrivate(mVolume);
- mDocumentsAndOtherPreference.setVisible(sharedVolume != null
- && sharedVolume.isMountedReadable());
+ mDocumentsAndOtherPreference.setVisible(mIsDocumentsPrefShown);
} else {
mDocumentsAndOtherPreference.setVisible(false);
}
}
+ private boolean isDocumentsPrefShown() {
+ VolumeInfo sharedVolume = mSvp.findEmulatedForPrivate(mVolume);
+ return sharedVolume != null && sharedVolume.isMountedReadable();
+ }
+
private void updatePrivateStorageCategoryPreferencesOrder() {
if (mScreen == null || !isValidPrivateVolume()) {
return;
@@ -360,44 +370,74 @@
mTrashPreference = screen.findPreference(TRASH_KEY);
}
- /** Fragments use it to set storage result and update UI of this controller. */
- public void onLoadFinished(SparseArray<StorageAsyncLoader.StorageResult> result, int userId) {
- final StorageAsyncLoader.StorageResult data = result.get(userId);
-
- mImagesPreference.setStorageSize(data.imagesSize, mTotalSize);
- mVideosPreference.setStorageSize(data.videosSize, mTotalSize);
- mAudioPreference.setStorageSize(data.audioSize, mTotalSize);
- mAppsPreference.setStorageSize(data.allAppsExceptGamesSize, mTotalSize);
- mGamesPreference.setStorageSize(data.gamesSize, mTotalSize);
- mDocumentsAndOtherPreference.setStorageSize(data.documentsAndOtherSize, mTotalSize);
- mTrashPreference.setStorageSize(data.trashSize, mTotalSize);
-
+ /**
+ * Fragments use it to set storage result and update UI of this controller.
+ * @param result The StorageResult from StorageAsyncLoader. This allows a nullable result.
+ * When it's null, the cached storage size info will be used instead.
+ * @param userId User ID to get the storage size info
+ */
+ public void onLoadFinished(@Nullable SparseArray<StorageAsyncLoader.StorageResult> result,
+ int userId) {
+ // Calculate the size info for each category
+ StorageCacheHelper.StorageCache storageCache = getSizeInfo(result, userId);
+ // Set size info to each preference
+ mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize);
+ mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize);
+ mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize);
+ mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize);
+ mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize);
+ mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize);
+ mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize);
if (mSystemPreference != null) {
- // Everything else that hasn't already been attributed is tracked as
- // belonging to system.
- long attributedSize = 0;
- for (int i = 0; i < result.size(); i++) {
- final StorageAsyncLoader.StorageResult otherData = result.valueAt(i);
- attributedSize +=
- otherData.gamesSize
- + otherData.audioSize
- + otherData.videosSize
- + otherData.imagesSize
- + otherData.documentsAndOtherSize
- + otherData.trashSize
- + otherData.allAppsExceptGamesSize;
- attributedSize -= otherData.duplicateCodeSize;
- }
-
- final long systemSize = Math.max(DataUnit.GIBIBYTES.toBytes(1),
- mUsedBytes - attributedSize);
- mSystemPreference.setStorageSize(systemSize, mTotalSize);
+ mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize);
+ }
+ // Cache the size info
+ if (result != null) {
+ mStorageCacheHelper.cacheSizeInfo(storageCache);
}
- updatePrivateStorageCategoryPreferencesOrder();
+ // Sort the preference according to size info in descending order
+ if (!mIsPreferenceOrderedBySize) {
+ updatePrivateStorageCategoryPreferencesOrder();
+ mIsPreferenceOrderedBySize = true;
+ }
setPrivateStorageCategoryPreferencesVisibility(true);
}
+ private StorageCacheHelper.StorageCache getSizeInfo(
+ SparseArray<StorageAsyncLoader.StorageResult> result, int userId) {
+ if (result == null) {
+ return mStorageCacheHelper.retrieveCachedSize();
+ }
+ StorageAsyncLoader.StorageResult data = result.get(userId);
+ StorageCacheHelper.StorageCache storageCache = new StorageCacheHelper.StorageCache();
+ storageCache.imagesSize = data.imagesSize;
+ storageCache.videosSize = data.videosSize;
+ storageCache.audioSize = data.audioSize;
+ storageCache.allAppsExceptGamesSize = data.allAppsExceptGamesSize;
+ storageCache.gamesSize = data.gamesSize;
+ storageCache.documentsAndOtherSize = data.documentsAndOtherSize;
+ storageCache.trashSize = data.trashSize;
+ // Everything else that hasn't already been attributed is tracked as
+ // belonging to system.
+ long attributedSize = 0;
+ for (int i = 0; i < result.size(); i++) {
+ final StorageAsyncLoader.StorageResult otherData = result.valueAt(i);
+ attributedSize +=
+ otherData.gamesSize
+ + otherData.audioSize
+ + otherData.videosSize
+ + otherData.imagesSize
+ + otherData.documentsAndOtherSize
+ + otherData.trashSize
+ + otherData.allAppsExceptGamesSize;
+ attributedSize -= otherData.duplicateCodeSize;
+ }
+ storageCache.systemSize = Math.max(DataUnit.GIBIBYTES.toBytes(1),
+ mUsedBytes - attributedSize);
+ return storageCache;
+ }
+
public void setUsedSize(long usedSizeBytes) {
mUsedBytes = usedSizeBytes;
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
index 5d5a2a5..d972575 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
@@ -26,8 +26,8 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import com.android.settingslib.widget.SettingsSpinnerPreference;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
index 7660353..872fa8a 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
@@ -18,6 +18,7 @@
import android.app.usage.StorageStatsManager;
import android.content.Context;
+import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -47,11 +48,13 @@
private UsageProgressBarPreference mUsageProgressBarPreference;
private StorageEntry mStorageEntry;
boolean mIsUpdateStateFromSelectedStorageEntry;
+ private StorageCacheHelper mStorageCacheHelper;
public StorageUsageProgressBarPreferenceController(Context context, String key) {
super(context, key);
mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
+ mStorageCacheHelper = new StorageCacheHelper(context, UserHandle.myUserId());
}
/** Set StorageEntry to display. */
@@ -71,6 +74,15 @@
}
private void getStorageStatsAndUpdateUi() {
+ // Use cached data for both total size and used size.
+ if (mStorageEntry != null && mStorageEntry.isMounted() && mStorageEntry.isPrivate()) {
+ StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
+ mTotalBytes = cachedData.totalSize;
+ mUsedBytes = cachedData.usedSize;
+ mIsUpdateStateFromSelectedStorageEntry = true;
+ updateState(mUsageProgressBarPreference);
+ }
+ // Get the latest data from StorageStatsManager.
ThreadUtils.postOnBackgroundThread(() -> {
try {
if (mStorageEntry == null || !mStorageEntry.isMounted()) {
diff --git a/src/com/android/settings/display/AutoRotatePreferenceController.java b/src/com/android/settings/display/AutoRotatePreferenceController.java
index 5dc2286..90423fb 100644
--- a/src/com/android/settings/display/AutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/AutoRotatePreferenceController.java
@@ -74,6 +74,7 @@
@Override
public int getAvailabilityStatus() {
return RotationPolicy.isRotationLockToggleVisible(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
new file mode 100644
index 0000000..fb6d9f4
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragment.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.Indexable;
+import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/** Fragment that shows all the available device state based auto-rotation preferences. */
+@SearchIndexable
+public class DeviceStateAutoRotateDetailsFragment extends DashboardFragment {
+
+ private static final String TAG = "DeviceStateAutoRotateDetailsFragment";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.device_state_auto_rotate_settings;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ DeviceStateAutoRotationHelper.initControllers(
+ getLifecycle(),
+ useAll(DeviceStateAutoRotateSettingController.class)
+ );
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return DeviceStateAutoRotationHelper.createPreferenceControllers(context);
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.device_state_auto_rotate_settings) {
+
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(Context context,
+ boolean enabled) {
+ return DeviceStateAutoRotationHelper.getRawDataToIndex(context, enabled);
+ }
+ };
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java b/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java
new file mode 100644
index 0000000..5e49bf3
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateOverviewController.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * The top-level preference controller for device state based auto-rotation settings.
+ *
+ * It doesn't do anything on its own besides showing/hiding. The toggling of the settings will
+ * always be done in the details screen when device state based auto-rotation is enabled.
+ */
+public class DeviceStateAutoRotateOverviewController extends BasePreferenceController {
+
+ /** Preference key for when it is used in "accessibility_system_controls.xml". */
+ private static final String ACCESSIBILITY_PREF_KEY = "device_state_auto_rotate_accessibility";
+
+ public DeviceStateAutoRotateOverviewController(Context context, String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isAvailableInternal() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ private boolean isAvailableInternal() {
+ return isA11yPage()
+ ? DeviceStateAutoRotationHelper.isDeviceStateRotationEnabledForA11y(mContext)
+ : DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext);
+ }
+
+ private boolean isA11yPage() {
+ return TextUtils.equals(getPreferenceKey(), ACCESSIBILITY_PREF_KEY);
+ }
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java
new file mode 100644
index 0000000..c8f6280
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotateSettingController.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import java.util.List;
+
+/** Controller for device state based auto rotation preferences. */
+public class DeviceStateAutoRotateSettingController extends TogglePreferenceController implements
+ LifecycleObserver {
+
+ private SwitchPreference mPreference;
+
+ private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager;
+ private final int mOrder;
+ private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+ mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
+ private final int mDeviceState;
+ private final String mDeviceStateDescription;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+
+ public DeviceStateAutoRotateSettingController(Context context, int deviceState,
+ String deviceStateDescription, int order) {
+ super(context, getPreferenceKeyForDeviceState(deviceState));
+ mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ mDeviceState = deviceState;
+ mDeviceStateDescription = deviceStateDescription;
+ mAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(context);
+ mOrder = order;
+ }
+
+ void init(Lifecycle lifecycle) {
+ lifecycle.addObserver(this);
+ }
+
+ @OnLifecycleEvent(ON_START)
+ void onStart() {
+ mAutoRotateSettingsManager.registerListener(mDeviceStateRotationLockSettingsListener);
+ }
+
+ @OnLifecycleEvent(ON_STOP)
+ void onStop() {
+ mAutoRotateSettingsManager.unregisterListener(mDeviceStateRotationLockSettingsListener);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreference = new SwitchPreference(mContext);
+ mPreference.setTitle(mDeviceStateDescription);
+ mPreference.setKey(getPreferenceKey());
+ mPreference.setOrder(mOrder);
+ screen.addPreference(mPreference);
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return getPreferenceKeyForDeviceState(mDeviceState);
+ }
+
+ private static String getPreferenceKeyForDeviceState(int deviceState) {
+ return "auto_rotate_device_state_" + deviceState;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return !mAutoRotateSettingsManager.isRotationLocked(mDeviceState);
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ boolean isRotationLocked = !isChecked;
+ mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATION_LOCK,
+ isRotationLocked);
+ mAutoRotateSettingsManager.updateSetting(mDeviceState, isRotationLocked);
+ return true;
+ }
+
+ @Override
+ public void updateRawDataToIndex(List<SearchIndexableRaw> rawData) {
+ SearchIndexableRaw indexable = new SearchIndexableRaw(mContext);
+ indexable.key = getPreferenceKey();
+ indexable.title = mDeviceStateDescription;
+ // Maybe pass screen title as param?
+ indexable.screenTitle = mContext.getString(R.string.accelerometer_title);
+ rawData.add(indexable);
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return true; // Maybe set to false if in accessibility settings screen
+ }
+
+ @Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+}
diff --git a/src/com/android/settings/display/DeviceStateAutoRotationHelper.java b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java
new file mode 100644
index 0000000..223ef1a
--- /dev/null
+++ b/src/com/android/settings/display/DeviceStateAutoRotationHelper.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.lifecycle.Lifecycle;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager.SettableDeviceState;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class with utility methods related to device state auto-rotation that can be used in
+ * auto-rotation settings fragments and controllers.
+ */
+public class DeviceStateAutoRotationHelper {
+
+ private static final String TAG = "DeviceStateAutoRotHelpr";
+
+ static void initControllers(Lifecycle lifecycle,
+ List<DeviceStateAutoRotateSettingController> controllers) {
+ for (DeviceStateAutoRotateSettingController controller : controllers) {
+ controller.init(lifecycle);
+ }
+ }
+
+ static ImmutableList<AbstractPreferenceController> createPreferenceControllers(
+ Context context) {
+ List<SettableDeviceState> settableDeviceStates = DeviceStateRotationLockSettingsManager
+ .getInstance(context).getSettableDeviceStates();
+ int numDeviceStates = settableDeviceStates.size();
+ if (numDeviceStates == 0) {
+ return ImmutableList.of();
+ }
+ String[] deviceStateSettingDescriptions = context.getResources().getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions);
+ if (numDeviceStates != deviceStateSettingDescriptions.length) {
+ Log.wtf(TAG,
+ "Mismatch between number of device states and device states descriptions.");
+ return ImmutableList.of();
+ }
+
+ ImmutableList.Builder<AbstractPreferenceController> controllers =
+ ImmutableList.builderWithExpectedSize(numDeviceStates);
+ for (int i = 0; i < numDeviceStates; i++) {
+ SettableDeviceState settableDeviceState = settableDeviceStates.get(i);
+ if (!settableDeviceState.isSettable()) {
+ continue;
+ }
+ // Preferences with a lower order will be showed first. Here we go below 0 to make sure
+ // we are shown before statically declared preferences in XML.
+ int order = -numDeviceStates + i;
+ controllers.add(new DeviceStateAutoRotateSettingController(
+ context,
+ settableDeviceState.getDeviceState(),
+ deviceStateSettingDescriptions[i],
+ order
+ ));
+ }
+ return controllers.build();
+ }
+
+ static List<SearchIndexableRaw> getRawDataToIndex(
+ Context context, boolean enabled) {
+ // Check what the "enabled" param is for
+ List<AbstractPreferenceController> controllers = createPreferenceControllers(context);
+ List<SearchIndexableRaw> rawData = new ArrayList<>();
+ for (AbstractPreferenceController controller : controllers) {
+ ((BasePreferenceController) controller).updateRawDataToIndex(rawData);
+ }
+ return rawData;
+ }
+
+ /** Returns whether the device state based auto-rotation settings are enabled. */
+ public static boolean isDeviceStateRotationEnabled(Context context) {
+ return RotationPolicy.isRotationLockToggleVisible(context)
+ && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context);
+ }
+
+ /**
+ * Returns whether the device state based auto-rotation settings are enabled for the
+ * accessibility settings page.
+ */
+ public static boolean isDeviceStateRotationEnabledForA11y(Context context) {
+ return RotationPolicy.isRotationSupported(context)
+ && DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(context);
+ }
+}
diff --git a/src/com/android/settings/display/SmartAutoRotateController.java b/src/com/android/settings/display/SmartAutoRotateController.java
index 76a222a..d29a64e 100644
--- a/src/com/android/settings/display/SmartAutoRotateController.java
+++ b/src/com/android/settings/display/SmartAutoRotateController.java
@@ -47,6 +47,7 @@
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
/**
* SmartAutoRotateController controls whether auto rotation is enabled
@@ -54,6 +55,8 @@
public class SmartAutoRotateController extends TogglePreferenceController implements
Preference.OnPreferenceChangeListener, LifecycleObserver {
+ protected Preference mPreference;
+
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final SensorPrivacyManager mPrivacyManager;
private final PowerManager mPowerManager;
@@ -63,7 +66,9 @@
updateState(mPreference);
}
};
- protected Preference mPreference;
+ private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager;
+ private final DeviceStateRotationLockSettingsManager.DeviceStateRotationLockSettingsListener
+ mDeviceStateRotationLockSettingsListener = () -> updateState(mPreference);
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
public SmartAutoRotateController(Context context, String preferenceKey) {
@@ -73,6 +78,8 @@
mPrivacyManager
.addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference));
mPowerManager = context.getSystemService(PowerManager.class);
+ mDeviceStateAutoRotateSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(
+ context);
}
public void init(Lifecycle lifecycle) {
@@ -89,6 +96,9 @@
}
protected boolean isRotationLocked() {
+ if (DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)) {
+ return mDeviceStateAutoRotateSettingsManager.isRotationLockedForAllStates();
+ }
return RotationPolicy.isRotationLocked(mContext);
}
@@ -127,6 +137,8 @@
};
}
RotationPolicy.registerRotationPolicyListener(mContext, mRotationPolicyListener);
+ mDeviceStateAutoRotateSettingsManager.registerListener(
+ mDeviceStateRotationLockSettingsListener);
}
@OnLifecycleEvent(ON_STOP)
@@ -136,6 +148,8 @@
RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
mRotationPolicyListener = null;
}
+ mDeviceStateAutoRotateSettingsManager.unregisterListener(
+ mDeviceStateRotationLockSettingsListener);
}
@Override
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
index bd8ee84..d02e336 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceController.java
@@ -77,6 +77,7 @@
@Override
public int getAvailabilityStatus() {
return RotationPolicy.isRotationLockToggleVisible(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
index 4737336..9fda03c 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
@@ -34,10 +34,14 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.widget.FooterPreference;
+import java.util.List;
+
/**
* Preference fragment used for auto rotation
*/
@@ -60,6 +64,15 @@
public void onAttach(Context context) {
super.onAttach(context);
use(SmartAutoRotateController.class).init(getLifecycle());
+ DeviceStateAutoRotationHelper.initControllers(
+ getLifecycle(),
+ useAll(DeviceStateAutoRotateSettingController.class)
+ );
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return DeviceStateAutoRotationHelper.createPreferenceControllers(context);
}
@Override
@@ -79,7 +92,9 @@
@VisibleForTesting
void createHeader(SettingsActivity activity) {
- if (isRotationResolverServiceAvailable(activity)) {
+ boolean deviceStateRotationEnabled =
+ DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(activity);
+ if (isRotationResolverServiceAvailable(activity) && !deviceStateRotationEnabled) {
final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
switchBar.setTitle(
getContext().getString(R.string.auto_rotate_settings_primary_switch_title));
@@ -127,5 +142,12 @@
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.auto_rotate_settings);
+ new BaseSearchIndexProvider(R.xml.auto_rotate_settings) {
+
+ @Override
+ public List<SearchIndexableRaw> getRawDataToIndex(
+ Context context, boolean enabled) {
+ return DeviceStateAutoRotationHelper.getRawDataToIndex(context, enabled);
+ }
+ };
}
diff --git a/src/com/android/settings/dream/DreamAdapter.java b/src/com/android/settings/dream/DreamAdapter.java
index 8d0e9d5..4e16ccb 100644
--- a/src/com/android/settings/dream/DreamAdapter.java
+++ b/src/com/android/settings/dream/DreamAdapter.java
@@ -48,6 +48,7 @@
private final ImageView mIconView;
private final TextView mTitleView;
private final ImageView mPreviewView;
+ private final ImageView mPreviewPlaceholderView;
private final Button mCustomizeButton;
private final Context mContext;
@@ -55,6 +56,7 @@
super(view);
mContext = context;
mPreviewView = view.findViewById(R.id.preview);
+ mPreviewPlaceholderView = view.findViewById(R.id.preview_placeholder);
mIconView = view.findViewById(R.id.icon);
mTitleView = view.findViewById(R.id.title_text);
mCustomizeButton = view.findViewById(R.id.customize_button);
@@ -68,10 +70,9 @@
final Drawable previewImage = item.getPreviewImage();
if (previewImage != null) {
- mPreviewView.setVisibility(View.VISIBLE);
mPreviewView.setImageDrawable(previewImage);
- } else {
- mPreviewView.setVisibility(View.GONE);
+ mPreviewView.setClipToOutline(true);
+ mPreviewPlaceholderView.setVisibility(View.GONE);
}
final Drawable icon = item.isActive()
diff --git a/src/com/android/settings/dream/DreamComplicationPickerController.java b/src/com/android/settings/dream/DreamComplicationPickerController.java
deleted file mode 100644
index 09ed8da..0000000
--- a/src/com/android/settings/dream/DreamComplicationPickerController.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 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.dream;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-
-import androidx.preference.PreferenceScreen;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.settings.R;
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.dream.DreamBackend;
-import com.android.settingslib.widget.LayoutPreference;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Controller for the component allowing a user to select overlays to show on top of dreams.
- */
-public class DreamComplicationPickerController extends BasePreferenceController {
- private static final String KEY = "dream_complication_picker";
-
- private final DreamBackend mBackend;
- private final Set<Integer> mSupportedComplications;
-
- private class ComplicationItem implements IDreamItem {
- private final int mComplicationType;
- private boolean mEnabled;
-
- ComplicationItem(@DreamBackend.ComplicationType int complicationType) {
- mComplicationType = complicationType;
- mEnabled = mBackend.isComplicationEnabled(mComplicationType);
- }
-
- @Override
- public CharSequence getTitle() {
- return mBackend.getComplicationTitle(mComplicationType);
- }
-
- @Override
- public Drawable getIcon() {
- // TODO(b/215703483): add icon for each complication
- return null;
- }
-
- @Override
- public void onItemClicked() {
- mEnabled = !mEnabled;
- mBackend.setComplicationEnabled(mComplicationType, mEnabled);
- }
-
- @Override
- public Drawable getPreviewImage() {
- // TODO(b/215703483): add preview image for each complication
- return null;
- }
-
- @Override
- public boolean isActive() {
- return mEnabled;
- }
- }
-
- public DreamComplicationPickerController(Context context) {
- super(context, KEY);
- mBackend = DreamBackend.getInstance(context);
- mSupportedComplications = mBackend.getSupportedComplications();
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY;
- }
-
- @Override
- public int getAvailabilityStatus() {
- return mSupportedComplications.size() > 0 ? AVAILABLE
- : CONDITIONALLY_UNAVAILABLE;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
-
- DreamAdapter adapter = new DreamAdapter(mSupportedComplications.stream()
- .map(ComplicationItem::new)
- .collect(Collectors.toList()));
-
- LayoutPreference pref = screen.findPreference(getPreferenceKey());
- if (pref != null) {
- final RecyclerView recyclerView = pref.findViewById(R.id.dream_list);
- recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
- recyclerView.setHasFixedSize(true);
- recyclerView.setAdapter(adapter);
- }
- }
-}
diff --git a/src/com/android/settings/dream/DreamComplicationPreferenceController.java b/src/com/android/settings/dream/DreamComplicationPreferenceController.java
new file mode 100644
index 0000000..d9c4fb3
--- /dev/null
+++ b/src/com/android/settings/dream/DreamComplicationPreferenceController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 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.dream;
+
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+import com.android.settingslib.dream.DreamBackend;
+
+/**
+ * Controller for the {@link androidx.preference.SwitchPreference} which controls if dream
+ * overlays should be enabled.
+ */
+public class DreamComplicationPreferenceController extends TogglePreferenceController {
+ private final DreamBackend mBackend;
+
+ public DreamComplicationPreferenceController(Context context, String key) {
+ super(context, key);
+ mBackend = DreamBackend.getInstance(context);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mBackend.getSupportedComplications().isEmpty() ? CONDITIONALLY_UNAVAILABLE
+ : AVAILABLE;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return mBackend.getEnabledComplications().containsAll(mBackend.getSupportedComplications());
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ for (int complication : mBackend.getSupportedComplications()) {
+ mBackend.setComplicationEnabled(complication, isChecked);
+ }
+ return true;
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+}
diff --git a/src/com/android/settings/dream/DreamPickerController.java b/src/com/android/settings/dream/DreamPickerController.java
index 6d5463c..b7ba614 100644
--- a/src/com/android/settings/dream/DreamPickerController.java
+++ b/src/com/android/settings/dream/DreamPickerController.java
@@ -21,7 +21,7 @@
import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
-import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
@@ -71,16 +71,21 @@
}
@Override
- public void updateState(Preference preference) {
- super.updateState(preference);
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
mAdapter = new DreamAdapter(mDreamInfos.stream()
.map(DreamItem::new)
.collect(Collectors.toList()));
- final RecyclerView recyclerView =
- ((LayoutPreference) preference).findViewById(R.id.dream_list);
+ final LayoutPreference pref = screen.findPreference(getPreferenceKey());
+ if (pref == null) {
+ return;
+ }
+ final RecyclerView recyclerView = pref.findViewById(R.id.dream_list);
recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
+ recyclerView.addItemDecoration(
+ new GridSpacingItemDecoration(mContext, R.dimen.dream_preference_card_padding));
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(mAdapter);
}
diff --git a/src/com/android/settings/dream/DreamSettings.java b/src/com/android/settings/dream/DreamSettings.java
index 2acce07..bfa19a9 100644
--- a/src/com/android/settings/dream/DreamSettings.java
+++ b/src/com/android/settings/dream/DreamSettings.java
@@ -34,14 +34,12 @@
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.WhenToDream;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
@SearchIndexable
@@ -52,7 +50,6 @@
static final String WHILE_DOCKED_ONLY = "while_docked_only";
static final String EITHER_CHARGING_OR_DOCKED = "either_charging_or_docked";
static final String NEVER_DREAM = "never";
- private static final String COMPLICATIONS_CATEGORY_KEY = "dream_complication_category";
@WhenToDream
static int getSettingFromPrefKey(String key) {
@@ -138,14 +135,8 @@
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
- final DreamComplicationPickerController complicationPickerController =
- new DreamComplicationPickerController(context);
-
- controllers.add(complicationPickerController);
- controllers.add(new WhenToDreamPreferenceController(context));
controllers.add(new DreamPickerController(context));
- controllers.add(new PreferenceCategoryController(context, COMPLICATIONS_CATEGORY_KEY)
- .setChildren(Collections.singletonList(complicationPickerController)));
+ controllers.add(new WhenToDreamPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/dream/GridSpacingItemDecoration.java b/src/com/android/settings/dream/GridSpacingItemDecoration.java
new file mode 100644
index 0000000..6e6a551
--- /dev/null
+++ b/src/com/android/settings/dream/GridSpacingItemDecoration.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 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.dream;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.annotation.DimenRes;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * RecyclerView item decorator to be used with {@link GridLayoutManager} for applying padding to
+ * only the inner elements of the grid.
+ */
+public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
+ private final int mSpacing;
+ private final boolean mRtl;
+
+ public GridSpacingItemDecoration(Context context, @DimenRes int spacingId) {
+ mSpacing = context.getResources().getDimensionPixelSize(spacingId);
+ mRtl = context.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ final RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
+
+ if (!(layoutManager instanceof GridLayoutManager)) {
+ return;
+ }
+
+ final int spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
+ final int position = parent.getChildAdapterPosition(view);
+ final int column = position % spanCount;
+
+ final int startPadding = column * mSpacing / spanCount;
+ final int endPadding = mSpacing - (column + 1) * mSpacing / spanCount;
+
+ outRect.left = mRtl ? endPadding : startPadding;
+ outRect.right = mRtl ? startPadding : endPadding;
+ if (position >= spanCount) {
+ outRect.top = mSpacing;
+ }
+ }
+}
diff --git a/packages/apps/Settings/src/com/android/settings/dream/OWNERS b/src/com/android/settings/dream/OWNERS
similarity index 100%
rename from packages/apps/Settings/src/com/android/settings/dream/OWNERS
rename to src/com/android/settings/dream/OWNERS
diff --git a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
index 815f2fd..8dbce79 100644
--- a/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/BatteryAppListPreferenceController.java
@@ -36,6 +36,7 @@
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.SparseArray;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -64,6 +65,7 @@
*/
public class BatteryAppListPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
+ private static final String TAG = "BatteryAppListPreferenceController";
@VisibleForTesting
static final boolean USE_FAKE_DATA = false;
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 20;
@@ -103,9 +105,14 @@
PowerProfile powerProfile = new PowerProfile(context);
// Cheap hack to try to figure out if the power_profile.xml was populated.
- return powerProfile.getAveragePowerForOrdinal(
- PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0)
- >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
+ final double averagePowerForOrdinal = powerProfile.getAveragePowerForOrdinal(
+ PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
+ final boolean shouldShowBatteryAttributionList =
+ averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
+ if (!shouldShowBatteryAttributionList) {
+ Log.w(TAG, "shouldShowBatteryAttributionList(): " + averagePowerForOrdinal);
+ }
+ return shouldShowBatteryAttributionList;
}
};
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index 71e65bf..436cde8 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -112,7 +112,8 @@
if (intent != null && mBatteryListener != null) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
final String batteryLevel = Utils.getBatteryPercentage(intent);
- final String batteryStatus = Utils.getBatteryStatus(mContext, intent);
+ final String batteryStatus =
+ Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false);
final int batteryHealth = intent.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
if (!Utils.isBatteryPresent(intent)) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index efb58a5..98f19fe 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -243,6 +243,8 @@
@NonNull BatteryUsageStats batteryUsageStats, Estimate estimate,
long elapsedRealtimeUs, boolean shortString) {
final long startTime = System.currentTimeMillis();
+ final boolean isCompactStatus = context.getResources().getBoolean(
+ com.android.settings.R.bool.config_use_compact_battery_status);
BatteryInfo info = new BatteryInfo();
info.mBatteryUsageStats = batteryUsageStats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
@@ -253,21 +255,21 @@
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
== BatteryManager.BATTERY_HEALTH_OVERHEAT;
- info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast);
+ info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
info.batteryStatus = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats,
- info);
+ info, isCompactStatus);
}
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
return info;
}
private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast,
- BatteryUsageStats stats, BatteryInfo info) {
+ BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) {
final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs();
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
@@ -291,7 +293,8 @@
R.string.power_remaining_charging_duration_only, timeString);
info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString);
} else {
- final String chargeStatusLabel = Utils.getBatteryStatus(context, batteryBroadcast);
+ final String chargeStatusLabel =
+ Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
info.remainingLabel = null;
info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString :
resources.getString(R.string.power_charging, info.batteryPercentString,
diff --git a/src/com/android/settings/gestures/OneHandedSettings.java b/src/com/android/settings/gestures/OneHandedSettings.java
index 3825896..0e6b402 100644
--- a/src/com/android/settings/gestures/OneHandedSettings.java
+++ b/src/com/android/settings/gestures/OneHandedSettings.java
@@ -26,6 +26,7 @@
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
+import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.ShortcutPreference;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.widget.IllustrationPreference;
@@ -64,7 +65,7 @@
mainSwitchPreference.addOnSwitchChangeListener((switchView, isChecked) -> {
switchView.setChecked(isChecked);
if (isChecked) {
- showQuickSettingsTooltipIfNeeded();
+ showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
}
});
}
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index c56d89b..051a54a 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -32,12 +32,17 @@
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toolbar;
+import androidx.core.graphics.Insets;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
@@ -96,7 +101,7 @@
void onHomepageLoaded();
}
- private interface FragmentBuilder<T extends Fragment> {
+ private interface FragmentBuilder<T extends Fragment> {
T build();
}
@@ -149,7 +154,9 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setupEdgeToEdge();
setContentView(R.layout.settings_homepage_container);
+
mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this);
mSplitController = SplitController.getInstance();
mIsTwoPane = mSplitController.isActivityEmbedded(this);
@@ -224,6 +231,24 @@
}
}
+ private void setupEdgeToEdge() {
+ WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(android.R.id.content),
+ (v, windowInsets) -> {
+ Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
+ // Apply the insets as a margin to the view. Here the system is setting
+ // only the top dimensions.
+ ViewGroup.MarginLayoutParams mlp =
+ (ViewGroup.MarginLayoutParams) v.getLayoutParams();
+ mlp.topMargin = insets.top;
+ v.setLayoutParams(mlp);
+
+ // Return CONSUMED if you don't want the window insets to keep being
+ // passed down to descendant views.
+ return WindowInsetsCompat.CONSUMED;
+ });
+ }
+
private void initSearchBarView() {
final Toolbar toolbar = findViewById(R.id.search_action_bar);
FeatureFactory.getFactory(this).getSearchFeatureProvider()
@@ -265,7 +290,7 @@
// Update status bar color
window.setStatusBarColor(color);
// Update content background.
- findViewById(R.id.settings_homepage_container).setBackgroundColor(color);
+ findViewById(android.R.id.content).setBackgroundColor(color);
}
private void showSuggestionFragment(boolean scrollNeeded) {
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index 7ce6730..734e079 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -119,6 +119,7 @@
? ((Instrumentable) caller).getMetricsCategory()
: Instrumentable.METRICS_CATEGORY_UNKNOWN)
.setTitleRes(-1)
+ .setIsSecondaryLayerPage(true)
.launch();
return true;
}
diff --git a/src/com/android/settings/network/EthernetTetherPreferenceController.java b/src/com/android/settings/network/EthernetTetherPreferenceController.java
index 5b2cab7..58c1fd2 100644
--- a/src/com/android/settings/network/EthernetTetherPreferenceController.java
+++ b/src/com/android/settings/network/EthernetTetherPreferenceController.java
@@ -21,52 +21,63 @@
import android.net.TetheringManager;
import android.os.Handler;
import android.os.Looper;
-import android.text.TextUtils;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.OnLifecycleEvent;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.HashSet;
+
/**
* This controller helps to manage the switch state and visibility of ethernet tether switch
* preference.
*/
public final class EthernetTetherPreferenceController extends TetherBasePreferenceController {
- private final String mEthernetRegex;
+ private final HashSet<String> mAvailableInterfaces = new HashSet<>();
private final EthernetManager mEthernetManager;
+
@VisibleForTesting
- EthernetManager.Listener mEthernetListener;
+ EthernetManager.InterfaceStateListener mEthernetListener;
public EthernetTetherPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
- mEthernetRegex = context.getString(
- com.android.internal.R.string.config_ethernet_iface_regex);
- mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);
+ mEthernetManager = context.getSystemService(EthernetManager.class);
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
- mEthernetListener = (iface, isAvailable) -> updateState(mPreference);
+ mEthernetListener = (iface, state, role, configuration) -> {
+ if (state == EthernetManager.STATE_LINK_UP) {
+ mAvailableInterfaces.add(iface);
+ } else {
+ mAvailableInterfaces.remove(iface);
+ }
+ updateState(mPreference);
+ };
final Handler handler = new Handler(Looper.getMainLooper());
// Executor will execute to post the updateState event to a new handler which is created
// from the main looper when the {@link EthernetManager.Listener.onAvailabilityChanged}
// is triggerd.
- mEthernetManager.addListener(mEthernetListener, r -> handler.post(r));
+ if (mEthernetManager != null) {
+ mEthernetManager.addInterfaceStateListener(r -> handler.post(r), mEthernetListener);
+ }
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
- mEthernetManager.removeListener(mEthernetListener);
- mEthernetListener = null;
+ if (mEthernetManager != null) {
+ mEthernetManager.removeInterfaceStateListener(mEthernetListener);
+ }
}
@Override
public boolean shouldEnable() {
+ ensureRunningOnMainLoopThread();
String[] available = mTm.getTetherableIfaces();
for (String s : available) {
- if (s.matches(mEthernetRegex)) {
+ if (mAvailableInterfaces.contains(s)) {
return true;
}
}
@@ -75,11 +86,19 @@
@Override
public boolean shouldShow() {
- return !TextUtils.isEmpty(mEthernetRegex);
+ return mEthernetManager != null;
}
@Override
public int getTetherType() {
return TetheringManager.TETHERING_ETHERNET;
}
+
+ private void ensureRunningOnMainLoopThread() {
+ if (Looper.getMainLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on main loop thread: "
+ + Thread.currentThread().getName());
+ }
+ }
}
diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
index 442af38..5c7c54e 100644
--- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
+++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
@@ -97,23 +97,19 @@
public PrivateDnsModeDialogPreference(Context context) {
super(context);
- initialize();
}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
- initialize();
}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- initialize();
}
public PrivateDnsModeDialogPreference(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- initialize();
}
private final AnnotationSpan.LinkInfo mUrlLinkInfo = new AnnotationSpan.LinkInfo(
@@ -131,12 +127,6 @@
}
});
- private void initialize() {
- // Add the "Restricted" icon resource so that if the preference is disabled by the
- // admin, an information button will be shown.
- setWidgetLayoutResource(R.layout.restricted_icon);
- }
-
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
@@ -146,13 +136,6 @@
// by the controller.
holder.itemView.setEnabled(true);
}
-
- final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
- if (restrictedIcon != null) {
- // Show the "Restricted" icon if, and only if, the preference was disabled by
- // the admin.
- restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
- }
}
@Override
diff --git a/src/com/android/settings/nfc/PaymentBackend.java b/src/com/android/settings/nfc/PaymentBackend.java
index 0cfed20..021d673 100644
--- a/src/com/android/settings/nfc/PaymentBackend.java
+++ b/src/com/android/settings/nfc/PaymentBackend.java
@@ -133,7 +133,9 @@
appInfo.settingsComponent = null;
}
appInfo.description = service.getDescription();
- appInfo.icon = pm.getUserBadgedIcon(service.loadIcon(pm), appInfo.userHandle);
+ Drawable icon = (service.loadBanner(pm) != null)
+ ? service.loadBanner(pm) : service.loadIcon(pm);
+ appInfo.icon = pm.getUserBadgedIcon(icon, appInfo.userHandle);
appInfos.add(appInfo);
}
diff --git a/src/com/android/settings/notification/NotificationLockscreenPreference.java b/src/com/android/settings/notification/NotificationLockscreenPreference.java
deleted file mode 100644
index b236014..0000000
--- a/src/com/android/settings/notification/NotificationLockscreenPreference.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.settings.notification;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ListView;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AlertDialog.Builder;
-
-import com.android.settings.R;
-import com.android.settings.RestrictedListPreference;
-import com.android.settings.Utils;
-import com.android.settingslib.RestrictedLockUtils;
-
-public class NotificationLockscreenPreference extends RestrictedListPreference {
-
- private boolean mAllowRemoteInput;
- private Listener mListener;
- private boolean mShowRemoteInput;
- private boolean mRemoteInputCheckBoxEnabled = true;
- private int mUserId = UserHandle.myUserId();
- private RestrictedLockUtils.EnforcedAdmin mAdminRestrictingRemoteInput;
-
- public NotificationLockscreenPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public void setRemoteInputCheckBoxEnabled(boolean enabled) {
- mRemoteInputCheckBoxEnabled = enabled;
- }
-
- public void setRemoteInputRestricted(RestrictedLockUtils.EnforcedAdmin admin) {
- mAdminRestrictingRemoteInput = admin;
- }
-
- @Override
- protected void onClick() {
- final Context context = getContext();
- if (!Utils.startQuietModeDialogIfNecessary(context, UserManager.get(context), mUserId)) {
- // Call super to create preference dialog only when work mode is on
- // startQuietModeDialogIfNecessary will return false if mUserId is not a managed user
- super.onClick();
- }
- }
-
- public void setUserId(int userId) {
- mUserId = userId;
- }
-
- @Override
- protected void onPrepareDialogBuilder(Builder builder,
- DialogInterface.OnClickListener innerListener) {
-
- mListener = new Listener(innerListener);
- builder.setSingleChoiceItems(createListAdapter(builder.getContext()), getSelectedValuePos(),
- mListener);
- mShowRemoteInput = getEntryValues().length == 3;
- mAllowRemoteInput = Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, 0) != 0;
- builder.setView(R.layout.lockscreen_remote_input);
- }
-
- @Override
- protected void onDialogCreated(Dialog dialog) {
- super.onDialogCreated(dialog);
- dialog.create();
- CheckBox checkbox = (CheckBox) dialog.findViewById(R.id.lockscreen_remote_input);
- checkbox.setChecked(!mAllowRemoteInput);
- checkbox.setOnCheckedChangeListener(mListener);
- checkbox.setEnabled(mAdminRestrictingRemoteInput == null);
-
- View restricted = dialog.findViewById(R.id.restricted_lock_icon_remote_input);
- restricted.setVisibility(mAdminRestrictingRemoteInput == null ? View.GONE : View.VISIBLE);
-
- if (mAdminRestrictingRemoteInput != null) {
- checkbox.setClickable(false);
- dialog.findViewById(com.android.internal.R.id.customPanel)
- .setOnClickListener(mListener);
- }
- }
-
- @Override
- protected void onDialogStateRestored(Dialog dialog, Bundle savedInstanceState) {
- super.onDialogStateRestored(dialog, savedInstanceState);
- ListView listView = ((AlertDialog) dialog).getListView();
- int selectedPosition = listView.getCheckedItemPosition();
-
- View panel = dialog.findViewById(com.android.internal.R.id.customPanel);
- panel.setVisibility(checkboxVisibilityForSelectedIndex(selectedPosition,
- mShowRemoteInput));
- mListener.setView(panel);
- }
-
- @Override
- protected void onDialogClosed(boolean positiveResult) {
- super.onDialogClosed(positiveResult);
- Settings.Secure.putInt(getContext().getContentResolver(),
- Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, mAllowRemoteInput ? 1 : 0);
- }
-
- @Override
- protected boolean isAutoClosePreference() {
- return false;
- }
-
- private int checkboxVisibilityForSelectedIndex(int selected,
- boolean showRemoteAtAll) {
- return selected == 1 && showRemoteAtAll && mRemoteInputCheckBoxEnabled ? View.VISIBLE
- : View.GONE;
- }
-
- private class Listener implements DialogInterface.OnClickListener,
- CompoundButton.OnCheckedChangeListener, View.OnClickListener {
-
- private final DialogInterface.OnClickListener mInner;
- private View mView;
-
- public Listener(DialogInterface.OnClickListener inner) {
- mInner = inner;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- mInner.onClick(dialog, which);
- ListView listView = ((AlertDialog) dialog).getListView();
- int selectedPosition = listView.getCheckedItemPosition();
- if (mView != null) {
- mView.setVisibility(
- checkboxVisibilityForSelectedIndex(selectedPosition, mShowRemoteInput));
- }
- }
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- mAllowRemoteInput = !isChecked;
- }
-
- public void setView(View view) {
- mView = view;
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == com.android.internal.R.id.customPanel) {
- RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
- mAdminRestrictingRemoteInput);
- }
- }
- }
-}
diff --git a/src/com/android/settings/privacy/PrivacyDashboardFragment.java b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
index 0638c60..df59bd5 100644
--- a/src/com/android/settings/privacy/PrivacyDashboardFragment.java
+++ b/src/com/android/settings/privacy/PrivacyDashboardFragment.java
@@ -29,7 +29,7 @@
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.notification.LockScreenNotificationPreferenceController;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -118,7 +118,7 @@
@Override
protected boolean isPageSearchEnabled(Context context) {
- return !SafetyCenterStatusHolder.get().isEnabled(context);
+ return !SafetyCenterManagerWrapper.get().isEnabled(context);
}
};
}
diff --git a/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java b/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java
index 87daf7f..b553239 100644
--- a/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java
+++ b/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceController.java
@@ -20,7 +20,7 @@
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
/** The preference controller for the top level privacy tile. */
public class TopLevelPrivacyEntryPreferenceController extends BasePreferenceController {
@@ -31,7 +31,7 @@
@Override
public int getAvailabilityStatus() {
- if (!SafetyCenterStatusHolder.get().isEnabled(mContext)) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(mContext)) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
diff --git a/src/com/android/settings/safetycenter/BiometricsSafetySource.java b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
index f37ea03..bfe2fb0 100644
--- a/src/com/android/settings/safetycenter/BiometricsSafetySource.java
+++ b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
@@ -21,13 +21,13 @@
/** Combined Biometrics Safety Source for Safety Center. */
public final class BiometricsSafetySource {
- public static final String SAFETY_SOURCE_ID = "BiometricsSafetySource";
+ public static final String SAFETY_SOURCE_ID = "Biometrics";
private BiometricsSafetySource() {}
/** Sends biometric safety data to Safety Center. */
public static void sendSafetyData(Context context) {
- if (!SafetyCenterStatusHolder.get().isEnabled(context)) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
return;
}
diff --git a/src/com/android/settings/safetycenter/LockScreenSafetySource.java b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
index 66001f2..5adaa79 100644
--- a/src/com/android/settings/safetycenter/LockScreenSafetySource.java
+++ b/src/com/android/settings/safetycenter/LockScreenSafetySource.java
@@ -17,51 +17,76 @@
package com.android.settings.safetycenter;
import android.app.PendingIntent;
-import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
+import android.os.UserHandle;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceStatus;
+import android.safetycenter.SafetySourceStatus.IconAction;
-import com.android.settings.core.SubSettingLauncher;
-import com.android.settings.password.ChooseLockGeneric;
-import com.android.settingslib.transition.SettingsTransitionHelper;
+import com.android.settings.R;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
/** Lock Screen Safety Source for Safety Center. */
public final class LockScreenSafetySource {
- public static final String SAFETY_SOURCE_ID = "LockScreenSafetySource";
+ public static final String SAFETY_SOURCE_ID = "LockScreen";
- private LockScreenSafetySource() {}
+ private LockScreenSafetySource() {
+ }
/** Sends lock screen safety data to Safety Center. */
- public static void sendSafetyData(Context context) {
- if (!SafetyCenterStatusHolder.get().isEnabled(context)) {
+ public static void sendSafetyData(Context context,
+ ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
return;
}
- // TODO(b/215515298): Replace placeholder SafetySourceData with real data.
- // TODO(b/217409995): Replace SECURITY_ALTERNATIVE with Safety Center metrics category.
- Intent clickIntent = new SubSettingLauncher(context)
- .setDestination(ChooseLockGeneric.ChooseLockGenericFragment.class.getName())
- .setSourceMetricsCategory(SettingsEnums.SECURITY_ALTERNATIVE)
- .setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
- .toIntent();
- PendingIntent pendingIntent = PendingIntent
+ if (!screenLockPreferenceDetailsUtils.isAvailable()) {
+ return;
+ }
+
+ final int userId = UserHandle.myUserId();
+ final RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtilsInternal
+ .checkIfPasswordQualityIsSet(context, userId);
+ final PendingIntent pendingIntent = createPendingIntent(context,
+ screenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent());
+ final IconAction gearMenuIconAction = createGearMenuIconAction(context,
+ screenLockPreferenceDetailsUtils);
+
+ final SafetySourceStatus status = new SafetySourceStatus.Builder(
+ context.getString(R.string.unlock_set_unlock_launch_picker_title),
+ screenLockPreferenceDetailsUtils.getSummary(UserHandle.myUserId()),
+ screenLockPreferenceDetailsUtils.isLockPatternSecure()
+ ? SafetySourceStatus.STATUS_LEVEL_OK
+ : SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION,
+ pendingIntent)
+ .setEnabled(
+ !screenLockPreferenceDetailsUtils.isPasswordQualityManaged(userId, admin))
+ .setIconAction(gearMenuIconAction).build();
+ final SafetySourceData safetySourceData = new SafetySourceData.Builder(
+ SAFETY_SOURCE_ID).setStatus(status).build();
+
+ SafetyCenterManagerWrapper.get().sendSafetyCenterUpdate(context, safetySourceData);
+ }
+
+ private static IconAction createGearMenuIconAction(Context context,
+ ScreenLockPreferenceDetailsUtils screenLockPreferenceDetailsUtils) {
+ return screenLockPreferenceDetailsUtils.shouldShowGearMenu() ? new IconAction(
+ IconAction.ICON_TYPE_GEAR,
+ createPendingIntent(context,
+ screenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent()))
+ : null;
+ }
+
+ private static PendingIntent createPendingIntent(Context context, Intent intent) {
+ return PendingIntent
.getActivity(
context,
0 /* requestCode */,
- clickIntent,
+ intent,
PendingIntent.FLAG_IMMUTABLE);
- SafetySourceData safetySourceData =
- new SafetySourceData.Builder(SAFETY_SOURCE_ID).setStatus(
- new SafetySourceStatus.Builder(
- "Lock Screen",
- "Lock screen settings",
- SafetySourceStatus.STATUS_LEVEL_OK,
- pendingIntent).build()
- ).build();
-
- SafetyCenterManagerWrapper.get().sendSafetyCenterUpdate(context, safetySourceData);
}
}
diff --git a/src/com/android/settings/safetycenter/SafetyCenterManagerWrapper.java b/src/com/android/settings/safetycenter/SafetyCenterManagerWrapper.java
index 7e47f23..e4a1d67 100644
--- a/src/com/android/settings/safetycenter/SafetyCenterManagerWrapper.java
+++ b/src/com/android/settings/safetycenter/SafetyCenterManagerWrapper.java
@@ -58,4 +58,24 @@
return;
}
}
+
+ /** Returns true is SafetyCenter page is enabled, false otherwise. */
+ public boolean isEnabled(Context context) {
+ if (context == null) {
+ Log.e(TAG, "Context is null at SafetyCenterManagerWrapper#isEnabled");
+ return false;
+ }
+ SafetyCenterManager safetyCenterManager =
+ context.getSystemService(SafetyCenterManager.class);
+ if (safetyCenterManager == null) {
+ Log.w(TAG, "System service SAFETY_CENTER_SERVICE (SafetyCenterManager) is null");
+ return false;
+ }
+ try {
+ return safetyCenterManager.isSafetyCenterEnabled();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Calling isSafetyCenterEnabled failed.", e);
+ return false;
+ }
+ }
}
diff --git a/src/com/android/settings/safetycenter/SafetyCenterStatusHolder.java b/src/com/android/settings/safetycenter/SafetyCenterStatusHolder.java
deleted file mode 100644
index 014c600..0000000
--- a/src/com/android/settings/safetycenter/SafetyCenterStatusHolder.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2022 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.safetycenter;
-
-import android.content.Context;
-import android.safetycenter.SafetyCenterManager;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/** Knows whether safety center is enabled or disabled. */
-public class SafetyCenterStatusHolder {
-
- private static final String TAG = "SafetyCenterStatusHolder";
-
- @VisibleForTesting
- public static SafetyCenterStatusHolder sInstance;
-
- private SafetyCenterStatusHolder() {}
-
- /** Returns an instance of {@link SafetyCenterStatusHolder}. */
- public static SafetyCenterStatusHolder get() {
- if (sInstance == null) {
- sInstance = new SafetyCenterStatusHolder();
- }
- return sInstance;
- }
-
- /** Returns true is SafetyCenter page is enabled, false otherwise. */
- public boolean isEnabled(Context context) {
- if (context == null) {
- Log.e(TAG, "Context is null at SafetyCenterStatusHolder#isEnabled");
- return false;
- }
- SafetyCenterManager safetyCenterManager =
- context.getSystemService(SafetyCenterManager.class);
- if (safetyCenterManager == null) {
- Log.w(TAG, "System service SAFETY_CENTER_SERVICE (SafetyCenterManager) is null");
- return false;
- }
- try {
- return safetyCenterManager.isSafetyCenterEnabled();
- } catch (RuntimeException e) {
- Log.e(TAG, "Calling isSafetyCenterEnabled failed.", e);
- return false;
- }
- }
-}
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index 55e8bba..a0d18c7 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -16,32 +16,59 @@
package com.android.settings.safetycenter;
+import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
+import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+
import com.google.common.collect.ImmutableList;
+import java.util.List;
+
/** Broadcast receiver for handling requests from Safety Center for fresh data. */
public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (!SafetyCenterStatusHolder.get().isEnabled(context)) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(context)) {
return;
}
- ImmutableList<String> sourceIds =
- ImmutableList.copyOf(intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS));
+ if (ACTION_REFRESH_SAFETY_SOURCES.equals(intent.getAction())) {
+ String[] sourceIdsExtra =
+ intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS);
+ if (sourceIdsExtra != null && sourceIdsExtra.length > 0) {
+ refreshSafetySources(context, ImmutableList.copyOf(sourceIdsExtra));
+ }
+ return;
+ }
+
+ if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ refreshAllSafetySources(context);
+ }
+ }
+
+ private static void refreshSafetySources(Context context, List<String> sourceIds) {
if (sourceIds.contains(LockScreenSafetySource.SAFETY_SOURCE_ID)) {
- LockScreenSafetySource.sendSafetyData(context);
+ LockScreenSafetySource.sendSafetyData(context,
+ new ScreenLockPreferenceDetailsUtils(context, SettingsEnums.SAFETY_CENTER));
}
if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID)) {
BiometricsSafetySource.sendSafetyData(context);
}
}
+
+ private static void refreshAllSafetySources(Context context) {
+ LockScreenSafetySource.sendSafetyData(context,
+ new ScreenLockPreferenceDetailsUtils(context, SettingsEnums.SAFETY_CENTER));
+ BiometricsSafetySource.sendSafetyData(context);
+ }
}
diff --git a/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java b/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java
index b00c084..af9509b 100644
--- a/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java
+++ b/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceController.java
@@ -37,7 +37,7 @@
@Override
public int getAvailabilityStatus() {
- if (SafetyCenterStatusHolder.get().isEnabled(mContext)) {
+ if (SafetyCenterManagerWrapper.get().isEnabled(mContext)) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
diff --git a/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java b/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
index abcacf0..a191acd 100644
--- a/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
+++ b/src/com/android/settings/security/ScreenLockPreferenceDetailsUtils.java
@@ -18,12 +18,14 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
+import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import androidx.annotation.StringRes;
+import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.Utils;
@@ -82,20 +84,34 @@
}
/**
+ * Returns whether the lock pattern is secure.
+ */
+ public boolean isLockPatternSecure() {
+ return mLockPatternUtils.isSecure(mUserId);
+ }
+
+ /**
* Returns whether the Gear Menu should be shown.
*/
public boolean shouldShowGearMenu() {
- return mLockPatternUtils.isSecure(mUserId);
+ return isLockPatternSecure();
}
/**
* Launches the {@link ScreenLockSettings}.
*/
public void openScreenLockSettings() {
- new SubSettingLauncher(mContext)
+ mContext.startActivity(getLaunchScreenLockSettingsIntent());
+ }
+
+ /**
+ * Returns {@link Intent} to launch the {@link ScreenLockSettings}.
+ */
+ public Intent getLaunchScreenLockSettingsIntent() {
+ return new SubSettingLauncher(mContext)
.setDestination(ScreenLockSettings.class.getName())
.setSourceMetricsCategory(mSourceMetricsCategory)
- .launch();
+ .toIntent();
}
/**
@@ -105,6 +121,29 @@
* @return true if the {@link ChooseLockGenericFragment} is launching.
*/
public boolean openChooseLockGenericFragment() {
+ final Intent quietModeDialogIntent = getQuietModeDialogIntent();
+ if (quietModeDialogIntent != null) {
+ mContext.startActivity(quietModeDialogIntent);
+ return false;
+ }
+ mContext.startActivity(getChooseLockGenericFragmentIntent());
+ return true;
+ }
+
+ /**
+ * Returns {@link Intent} to launch an appropriate Settings screen.
+ *
+ * <p>If Quiet Mode is enabled for managed profile, returns {@link Intent} to launch a dialog
+ * to disable the Quiet Mode, otherwise returns {@link Intent} to launch
+ * {@link ChooseLockGenericFragment}.
+ */
+ public Intent getLaunchChooseLockGenericFragmentIntent() {
+ final Intent quietModeDialogIntent = getQuietModeDialogIntent();
+ return quietModeDialogIntent != null ? quietModeDialogIntent
+ : getChooseLockGenericFragmentIntent();
+ }
+
+ private Intent getQuietModeDialogIntent() {
// TODO(b/35930129): Remove once existing password can be passed into vold directly.
// Currently we need this logic to ensure that the QUIET_MODE is off for any work
// profile with unified challenge on FBE-enabled devices. Otherwise, vold would not be
@@ -112,17 +151,20 @@
if (mProfileChallengeUserId != UserHandle.USER_NULL
&& !mLockPatternUtils.isSeparateProfileChallengeEnabled(mProfileChallengeUserId)
&& StorageManager.isFileEncryptedNativeOnly()) {
- if (Utils.startQuietModeDialogIfNecessary(mContext, mUm, mProfileChallengeUserId)) {
- return false;
+ if (mUm.isQuietModeEnabled(UserHandle.of(mProfileChallengeUserId))) {
+ return UnlaunchableAppActivity.createInQuietModeDialogIntent(
+ mProfileChallengeUserId);
}
}
+ return null;
+ }
- new SubSettingLauncher(mContext)
+ private Intent getChooseLockGenericFragmentIntent() {
+ return new SubSettingLauncher(mContext)
.setDestination(ChooseLockGenericFragment.class.getName())
.setSourceMetricsCategory(mSourceMetricsCategory)
.setTransitionType(SettingsTransitionHelper.TransitionType.TRANSITION_SLIDE)
- .launch();
- return true;
+ .toIntent();
}
@StringRes
diff --git a/src/com/android/settings/security/SecurityAdvancedSettings.java b/src/com/android/settings/security/SecurityAdvancedSettings.java
index e5f4945..f716064 100644
--- a/src/com/android/settings/security/SecurityAdvancedSettings.java
+++ b/src/com/android/settings/security/SecurityAdvancedSettings.java
@@ -34,7 +34,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintProfileStatusPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.security.trustagent.TrustAgentListPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
@@ -92,7 +92,7 @@
final Context context = getContext();
if (context == null) {
return CATEGORY_SECURITY_LEGACY_ADVANCED_SETTINGS;
- } else if (SafetyCenterStatusHolder.get().isEnabled(context)) {
+ } else if (SafetyCenterManagerWrapper.get().isEnabled(context)) {
return CategoryKey.CATEGORY_SECURITY_ADVANCED_SETTINGS;
} else {
final SecuritySettingsFeatureProvider securitySettingsFeatureProvider =
diff --git a/src/com/android/settings/security/SecuritySettings.java b/src/com/android/settings/security/SecuritySettings.java
index 6bfbf6c..6aadee6 100644
--- a/src/com/android/settings/security/SecuritySettings.java
+++ b/src/com/android/settings/security/SecuritySettings.java
@@ -25,7 +25,7 @@
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.security.trustagent.TrustAgentListPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
@@ -129,7 +129,7 @@
protected boolean isPageSearchEnabled(Context context) {
return !FeatureFactory.getFactory(context).getSecuritySettingsFeatureProvider()
.hasAlternativeSecuritySettingsFragment()
- && !SafetyCenterStatusHolder.get().isEnabled(context);
+ && !SafetyCenterManagerWrapper.get().isEnabled(context);
}
};
}
diff --git a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
index 11d1dc3..3cf40a0 100644
--- a/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
+++ b/src/com/android/settings/security/TopLevelSecurityEntryPreferenceController.java
@@ -24,7 +24,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
public class TopLevelSecurityEntryPreferenceController extends BasePreferenceController {
@@ -38,7 +38,7 @@
@Override
public int getAvailabilityStatus() {
- if (!SafetyCenterStatusHolder.get().isEnabled(mContext)) {
+ if (!SafetyCenterManagerWrapper.get().isEnabled(mContext)) {
return AVAILABLE;
}
return CONDITIONALLY_UNAVAILABLE;
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 3ea1502..9878b44 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -737,7 +737,8 @@
if (newUserIcon != userIcon) {
ThreadUtils.postOnBackgroundThread(() ->
mUserManager.setUserIcon(user.id,
- UserIcons.convertToBitmap(newUserIcon)));
+ UserIcons.convertToBitmapAtUserIconSize(
+ activity.getResources(), newUserIcon)));
mMePreference.setIcon(newUserIcon);
}
@@ -891,7 +892,9 @@
if (newUserIcon == null) {
newUserIcon = UserIcons.getDefaultUserIcon(getResources(), user.id, false);
}
- mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
+ mUserManager.setUserIcon(
+ user.id, UserIcons.convertToBitmapAtUserIconSize(
+ getResources(), newUserIcon));
if (mUserType == USER_TYPE_USER) {
mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
@@ -1315,7 +1318,7 @@
// Try finding the corresponding bitmap in the dark bitmap cache
bitmap = sDarkDefaultUserBitmapCache.get(userId);
if (bitmap == null) {
- bitmap = UserIcons.convertToBitmap(
+ bitmap = UserIcons.convertToBitmapAtUserIconSize(resources,
UserIcons.getDefaultUserIcon(resources, userId, false));
// Save it to cache
sDarkDefaultUserBitmapCache.put(userId, bitmap);
diff --git a/src/com/android/settings/widget/RestrictedAppPreference.java b/src/com/android/settings/widget/RestrictedAppPreference.java
index cd95357..f93b935 100644
--- a/src/com/android/settings/widget/RestrictedAppPreference.java
+++ b/src/com/android/settings/widget/RestrictedAppPreference.java
@@ -20,12 +20,10 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.view.View;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceViewHolder;
-import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreferenceHelper;
import com.android.settingslib.widget.AppPreference;
@@ -55,7 +53,6 @@
}
private void initialize(AttributeSet attrs, String userRestriction) {
- setWidgetLayoutResource(R.layout.restricted_icon);
mHelper = new RestrictedPreferenceHelper(getContext(), this, attrs);
this.userRestriction = userRestriction;
}
@@ -64,10 +61,6 @@
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
mHelper.onBindViewHolder(holder);
- final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
- if (restrictedIcon != null) {
- restrictedIcon.setVisibility(isDisabledByAdmin() ? View.VISIBLE : View.GONE);
- }
}
@Override
diff --git a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
index dd0c20a..6f26826 100644
--- a/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
+++ b/src/com/android/settings/wifi/details/WifiNetworkDetailsFragment.java
@@ -100,7 +100,6 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- setIfOnlyAvailableForAdmins(true);
mIsUiRestricted = isUiRestricted();
}
diff --git a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
index aec31fc..01ec42e 100644
--- a/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/AllInOneTetherSettingsTest.java
@@ -33,6 +33,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.EthernetManager;
import android.net.TetheringManager;
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiManager;
@@ -84,6 +85,8 @@
private PreferenceScreen mPreferenceScreen;
@Mock
private PreferenceGroup mWifiTetherGroup;
+ @Mock
+ private EthernetManager mEthernetManager;
@Before
public void setUp() {
@@ -95,6 +98,7 @@
.when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE);
doReturn(mTetheringManager)
.when(mContext).getSystemService(Context.TETHERING_SERVICE);
+ doReturn(mEthernetManager).when(mContext).getSystemService(EthernetManager.class);
doReturn(WIFI_REGEXS).when(mTetheringManager).getTetherableWifiRegexs();
doReturn(USB_REGEXS).when(mTetheringManager).getTetherableUsbRegexs();
doReturn(BT_REGEXS).when(mTetheringManager).getTetherableBluetoothRegexs();
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
index f595452..a455648 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityHearingAidPreferenceControllerTest.java
@@ -195,7 +195,7 @@
private void setupBluetoothEnvironment() {
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
- mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindowTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindowTest.java
index be67977..3fec81a 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindowTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityQuickSettingsTooltipWindowTest.java
@@ -53,6 +53,7 @@
private PopupWindow.OnDismissListener mMockOnDismissListener;
private static final String TEST_PACKAGE_NAME = "com.test.package";
+ private static final int TEST_RES_ID = 1234;
private final Context mContext = ApplicationProvider.getApplicationContext();
private AccessibilityQuickSettingsTooltipWindow mTooltipView;
private View mView;
@@ -66,8 +67,8 @@
@Test
public void initTooltipView_atMostAvailableTextWidth() {
final String quickSettingsTooltipsContent = mContext.getString(
- R.string.accessibility_service_quick_settings_tooltips_content, TEST_PACKAGE_NAME);
- mTooltipView.setup(quickSettingsTooltipsContent);
+ R.string.accessibility_service_qs_tooltips_content, TEST_PACKAGE_NAME);
+ mTooltipView.setup(quickSettingsTooltipsContent, TEST_RES_ID);
final int getMaxWidth = mTooltipView.getAvailableWindowWidth();
assertThat(mTooltipView.getWidth()).isAtMost(getMaxWidth);
@@ -75,7 +76,7 @@
@Test
public void showTooltipView_success() {
- mTooltipView.setup(TEST_PACKAGE_NAME);
+ mTooltipView.setup(TEST_PACKAGE_NAME, TEST_RES_ID);
assertThat(getLatestPopupWindow()).isNull();
mTooltipView.showAtTopCenter(mView);
@@ -85,7 +86,7 @@
@Test
public void accessibilityClickActionOnTooltipViewShown_shouldInvokeCallbackAndNotShowing() {
- mTooltipView.setup(TEST_PACKAGE_NAME);
+ mTooltipView.setup(TEST_PACKAGE_NAME, TEST_RES_ID);
mTooltipView.setOnDismissListener(mMockOnDismissListener);
mTooltipView.showAtTopCenter(mView);
@@ -101,7 +102,7 @@
@Test
public void dismiss_tooltipViewShown_shouldInvokeCallbackAndNotShowing() {
- mTooltipView.setup(TEST_PACKAGE_NAME);
+ mTooltipView.setup(TEST_PACKAGE_NAME, TEST_RES_ID);
mTooltipView.setOnDismissListener(mMockOnDismissListener);
mTooltipView.showAtTopCenter(mView);
@@ -113,7 +114,7 @@
@Test
public void waitAutoCloseDelayTime_tooltipViewShown_shouldInvokeCallbackAndNotShowing() {
- mTooltipView.setup(TEST_PACKAGE_NAME, /* closeDelayTimeMillis= */ 1);
+ mTooltipView.setup(TEST_PACKAGE_NAME, TEST_RES_ID, /* closeDelayTimeMillis= */ 1);
mTooltipView.setOnDismissListener(mMockOnDismissListener);
mTooltipView.showAtTopCenter(mView);
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
index ea0f317..d05600a 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
@@ -18,6 +18,7 @@
import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_QS_TOOLTIP_RESHOW;
import static com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment.KEY_SAVED_USER_SHORTCUT_TYPE;
+import static com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import static com.google.common.truth.Truth.assertThat;
@@ -204,7 +205,7 @@
@Config(shadows = ShadowFragment.class)
public void restoreValueFromSavedInstanceState_showTooltipView() {
mContext.setTheme(R.style.Theme_AppCompat);
- mFragment.showQuickSettingsTooltipIfNeeded();
+ mFragment.showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_EDIT);
assertThat(getLatestPopupWindow().isShowing()).isTrue();
final Bundle savedInstanceState = new Bundle();
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextAndDisplayFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
similarity index 89%
rename from tests/robotests/src/com/android/settings/accessibility/TextAndDisplayFragmentTest.java
rename to tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
index 96756ecd..0bb227e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextAndDisplayFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ColorAndMotionFragmentTest.java
@@ -32,17 +32,17 @@
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-public class TextAndDisplayFragmentTest {
+public class ColorAndMotionFragmentTest {
private Context mContext = ApplicationProvider.getApplicationContext();
@Test
public void getNonIndexableKeys_existInXmlLayout() {
- final List<String> niks = TextAndDisplayFragment.SEARCH_INDEX_DATA_PROVIDER
+ final List<String> niks = ColorAndMotionFragment.SEARCH_INDEX_DATA_PROVIDER
.getNonIndexableKeys(mContext);
final List<String> keys =
XmlTestUtils.getKeysFromPreferenceXml(mContext,
- R.xml.accessibility_text_and_display);
+ R.xml.accessibility_color_and_motion);
assertThat(keys).containsAtLeastElementsIn(niks);
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
index ef8f569..f908b8a 100644
--- a/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/LockScreenRotationPreferenceControllerTest.java
@@ -26,6 +26,7 @@
import com.android.internal.view.RotationPolicy;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import org.junit.Before;
@@ -50,7 +51,10 @@
}
@Test
- @Config(shadows = {ShadowRotationPolicy.class})
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ })
public void getAvailabilityStatus_supportedRotation_shouldReturnAvailable() {
ShadowRotationPolicy.setRotationSupported(true /* supported */);
@@ -59,8 +63,23 @@
}
@Test
- @Config(shadows = {ShadowRotationPolicy.class})
- public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() {
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(true /* supported */);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ @Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+ }) public void getAvailabilityStatus_unsupportedRotation_shouldReturnUnsupportedOnDevice() {
ShadowRotationPolicy.setRotationSupported(false /* supported */);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
diff --git a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
index 00cf052..c9bb687 100644
--- a/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/combination/CombinedBiometricStatusPreferenceControllerTest.java
@@ -18,10 +18,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -42,7 +39,6 @@
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;
@@ -96,7 +92,6 @@
mContext, TEST_PREF_KEY, mLifecycle);
}
- @Ignore
@Test
public void updateState_parentalConsentRequired_preferenceDisabled() {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
@@ -106,16 +101,10 @@
RestrictedLockUtils.EnforcedAdmin admin = mock(RestrictedLockUtils.EnforcedAdmin.class);
mController.mPreference = restrictedPreference;
- mController.updateStateInternal(admin, true, true);
+ mController.updateStateInternal(admin);
verify(restrictedPreference).setDisabledByAdmin(eq(admin));
- mController.updateStateInternal(admin, true, false);
- verify(restrictedPreference).setDisabledByAdmin(eq(null));
-
- mController.updateStateInternal(admin, false, true);
- verify(restrictedPreference).setDisabledByAdmin(eq(null));
-
- mController.updateStateInternal(admin, false, false);
+ mController.updateStateInternal(null);
verify(restrictedPreference).setDisabledByAdmin(eq(null));
}
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
index 31687c6..51cad70 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java
@@ -99,6 +99,7 @@
mController.mBluetoothAdapter = mBluetoothAdapter;
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
+ when(mCachedDevice.getIdentityAddress()).thenReturn(MAC_ADDRESS);
}
@Test
@@ -386,34 +387,52 @@
}
@Test
- public void showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showView() {
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_left));
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_middle));
- mController.showBatteryPredictionIfNecessary(1, 14218009,
- mLayoutPreference.findViewById(R.id.layout_right));
+ public void estimateReadyIsBothAvailable_showsView() {
+ mController.mIsLeftDeviceEstimateReady = true;
+ mController.mIsRightDeviceEstimateReady = true;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.VISIBLE);
- assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_middle),
- View.VISIBLE);
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
View.VISIBLE);
}
@Test
- public void showBatteryPredictionIfNecessary_estimateReadyIsNotAvailable_notShowView() {
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_left));
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_middle));
- mController.showBatteryPredictionIfNecessary(0, 14218009,
- mLayoutPreference.findViewById(R.id.layout_right));
+ public void leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = true;
+ mController.mIsRightDeviceEstimateReady = false;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.GONE);
- assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_middle),
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
+ View.GONE);
+ }
+
+ @Test
+ public void leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = false;
+ mController.mIsRightDeviceEstimateReady = true;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
+
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
+ View.GONE);
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
+ View.GONE);
+ }
+
+ @Test
+ public void bothDevicesEstimateIsNotReady_notShowView() {
+ mController.mIsLeftDeviceEstimateReady = false;
+ mController.mIsRightDeviceEstimateReady = false;
+
+ mController.showBothDevicesBatteryPredictionIfNecessary();
+
+ assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
View.GONE);
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
View.GONE);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
index 6401388..08f7084 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsControllerTestBase.java
@@ -76,7 +76,7 @@
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = spy(new Lifecycle(mLifecycleOwner));
- mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
}
@@ -155,6 +155,7 @@
mDevice = mBluetoothAdapter.getRemoteDevice(config.getAddress());
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(config.getAddress());
+ when(mCachedDevice.getIdentityAddress()).thenReturn(config.getAddress());
}
/**
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
index ce41a8d..fac8b58 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragmentTest.java
@@ -89,6 +89,7 @@
when(fragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
+ when(mCachedDevice.getIdentityAddress()).thenReturn(TEST_ADDRESS);
Bundle args = new Bundle();
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
mFragment.setArguments(args);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java
index 6da9bf4..88e15eb 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingControllerTest.java
@@ -24,6 +24,7 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
+import android.os.Parcel;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
@@ -40,12 +41,22 @@
@Config(shadows = {ShadowBluetoothAdapter.class})
public class BluetoothPairingControllerTest {
private final BluetoothClass mBluetoothClass =
- new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
+ createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
@Mock
private BluetoothDevice mBluetoothDevice;
private Context mContext;
private BluetoothPairingController mBluetoothPairingController;
+ private BluetoothClass createBtClass(int deviceClass) {
+ Parcel p = Parcel.obtain();
+ p.writeInt(deviceClass);
+ p.setDataPosition(0); // reset position of parcel before passing to constructor
+
+ BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
+ p.recycle();
+ return bluetoothClass;
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
index ea841fa..bac868f 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothPairingDetailTest.java
@@ -305,6 +305,7 @@
when(cachedDevice.isConnected()).thenReturn(true);
when(cachedDevice.getDevice()).thenReturn(device2);
when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
+ when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
BluetoothAdapter.STATE_CONNECTED);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ForgetDeviceDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/ForgetDeviceDialogFragmentTest.java
index 5c23238..9c266c1 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ForgetDeviceDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ForgetDeviceDialogFragmentTest.java
@@ -73,6 +73,7 @@
FakeFeatureFactory.setupForTest();
String deviceAddress = "55:66:77:88:99:AA";
when(mCachedDevice.getAddress()).thenReturn(deviceAddress);
+ when(mCachedDevice.getIdentityAddress()).thenReturn(deviceAddress);
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
mFragment = spy(ForgetDeviceDialogFragment.newInstance(deviceAddress));
diff --git a/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java
index c8467b2..ad5b70c 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/RemoteDeviceNameDialogFragmentTest.java
@@ -62,6 +62,7 @@
String deviceAddress = "55:66:77:88:99:AA";
when(mCachedDevice.getAddress()).thenReturn(deviceAddress);
+ when(mCachedDevice.getIdentityAddress()).thenReturn(deviceAddress);
mFragment = spy(RemoteDeviceNameDialogFragment.newInstance(mCachedDevice));
doReturn(mCachedDevice).when(mFragment).getDevice(any());
}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index fd1c8ff..aa5f980 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -144,6 +144,21 @@
}
@Test
+ public void useAll_returnsAllControllersOfType() {
+ final TestPreferenceController controller1 = new TestPreferenceController(mContext);
+ final TestPreferenceController controller2 = new TestPreferenceController(mContext);
+ final SubTestPreferenceController controller3 = new SubTestPreferenceController(mContext);
+ mTestFragment.addPreferenceController(controller1);
+ mTestFragment.addPreferenceController(controller2);
+ mTestFragment.addPreferenceController(controller3);
+
+ final List<TestPreferenceController> retrievedControllers = mTestFragment.useAll(
+ TestPreferenceController.class);
+
+ assertThat(retrievedControllers).containsExactly(controller1, controller2);
+ }
+
+ @Test
public void displayTilesAsPreference_shouldAddTilesWithIntent() {
when(mFakeFeatureFactory.dashboardFeatureProvider
.getTilesForCategory(nullable(String.class)))
@@ -360,6 +375,13 @@
}
}
+ public static class SubTestPreferenceController extends TestPreferenceController {
+
+ private SubTestPreferenceController(Context context) {
+ super(context);
+ }
+ }
+
private static class TestFragment extends DashboardFragment {
private final PreferenceManager mPreferenceManager;
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageCacheHelperTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageCacheHelperTest.java
new file mode 100644
index 0000000..1956b09
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageCacheHelperTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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.UserHandle;
+
+import androidx.test.core.app.ApplicationProvider;
+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 StorageCacheHelperTest {
+ private static final long FAKE_IMAGES_SIZE = 7000L;
+ private static final long FAKE_VIDEOS_SIZE = 8900L;
+ private static final long FAKE_AUDIO_SIZE = 3500L;
+ private static final long FAKE_APPS_SIZE = 4000L;
+ private static final long FAKE_GAMES_SIZE = 5000L;
+ private static final long FAKE_DOCS_SIZE = 1500L;
+ private static final long FAKE_TRASH_SIZE = 500L;
+ private static final long FAKE_SYSTEM_SIZE = 2300L;
+ private static final long FAKE_TOTAL_SIZE = 256000L;
+ private static final long FAKE_USED_SIZE = 50000L;
+
+ private Context mContext;
+ private StorageCacheHelper mHelper;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mHelper = new StorageCacheHelper(mContext, UserHandle.myUserId());
+ }
+
+ @Test
+ public void hasCachedSizeInfo_noCacheData_shouldReturnFalse() {
+ assertThat(mHelper.hasCachedSizeInfo()).isFalse();
+ }
+
+ @Test
+ public void hasCachedSizeInfo_hasCacheData_shouldReturnTrue() {
+ mHelper.cacheSizeInfo(getFakeStorageCache());
+
+ assertThat(mHelper.hasCachedSizeInfo()).isTrue();
+ }
+
+ @Test
+ public void cacheSizeInfo_shouldSaveToSharedPreference() {
+ mHelper.cacheSizeInfo(getFakeStorageCache());
+
+ StorageCacheHelper.StorageCache storageCache = mHelper.retrieveCachedSize();
+
+ assertThat(storageCache.imagesSize).isEqualTo(FAKE_IMAGES_SIZE);
+ assertThat(storageCache.totalSize).isEqualTo(0);
+ }
+
+ @Test
+ public void cacheTotalSizeAndUsedSize_shouldSaveToSharedPreference() {
+ mHelper.cacheTotalSizeAndUsedSize(FAKE_TOTAL_SIZE, FAKE_USED_SIZE);
+
+ StorageCacheHelper.StorageCache storageCache = mHelper.retrieveCachedSize();
+
+ assertThat(storageCache.totalSize).isEqualTo(FAKE_TOTAL_SIZE);
+ assertThat(storageCache.usedSize).isEqualTo(FAKE_USED_SIZE);
+ }
+
+ private StorageCacheHelper.StorageCache getFakeStorageCache() {
+ StorageCacheHelper.StorageCache result = new StorageCacheHelper.StorageCache();
+ result.trashSize = FAKE_TRASH_SIZE;
+ result.systemSize = FAKE_SYSTEM_SIZE;
+ result.imagesSize = FAKE_IMAGES_SIZE;
+ result.documentsAndOtherSize = FAKE_DOCS_SIZE;
+ result.audioSize = FAKE_AUDIO_SIZE;
+ result.gamesSize = FAKE_GAMES_SIZE;
+ result.videosSize = FAKE_VIDEOS_SIZE;
+ result.allAppsExceptGamesSize = FAKE_APPS_SIZE;
+ return result;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
index 1d175de..54e6b99 100644
--- a/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AutoRotatePreferenceControllerTest.java
@@ -30,6 +30,7 @@
import androidx.preference.SwitchPreference;
+import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -64,6 +65,7 @@
mPreference = new SwitchPreference(RuntimeEnvironment.application);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ disableDeviceStateRotation();
mController = new AutoRotatePreferenceController(mContext, "auto_rotate");
}
@@ -112,6 +114,26 @@
}
@Test
+ public void getAvailabilityStatus_deviceRotationDisabled_returnsAvailable() {
+ enableAutoRotationPreference();
+ disableDeviceStateRotation();
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(BasePreferenceController.AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceRotationEnabled_returnsUnsupported() {
+ enableAutoRotationPreference();
+ enableDeviceStateRotation();
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
public void testIsCheck() {
assertThat(mController.isChecked()).isFalse();
@@ -180,4 +202,15 @@
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT);
}
+
+ private void enableDeviceStateRotation() {
+ when(mContext.getResources().getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ new String[]{"0:0", "1:1", "2:2"});
+ }
+
+ private void disableDeviceStateRotation() {
+ when(mContext.getResources().getStringArray(
+ R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(new String[]{});
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
new file mode 100644
index 0000000..b773657
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateDetailsFragmentTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class DeviceStateAutoRotateDetailsFragmentTest {
+
+ private final DeviceStateAutoRotateDetailsFragment mFragment =
+ spy(new DeviceStateAutoRotateDetailsFragment());
+ private final Context mContext = spy(RuntimeEnvironment.application);
+ private final Resources mResources = spy(mContext.getResources());
+
+ @Before
+ public void setUp() throws Exception {
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationContext()).thenReturn(mContext);
+ when(mFragment.getContext()).thenReturn(mContext);
+ when(mFragment.getResources()).thenReturn(mResources);
+ }
+
+ @Test
+ public void getMetricsCategory_returnsAutoRotateSettings() {
+ assertThat(mFragment.getMetricsCategory()).isEqualTo(
+ SettingsEnums.DISPLAY_AUTO_ROTATE_SETTINGS);
+ }
+
+ @Test
+ public void getPreferenceScreenResId_returnsDeviceStateAutoRotationSettings() {
+ assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(
+ R.xml.device_state_auto_rotate_settings);
+ }
+
+ @Test
+ public void createPreferenceControllers_settableDeviceStates_returnsDeviceStateControllers() {
+ enableDeviceStateSettableRotationStates(new String[]{"0:1", "1:1"},
+ new String[]{"Folded", "Unfolded"});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).hasSize(2);
+ assertThat(preferenceControllers.get(0)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ assertThat(preferenceControllers.get(1)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ }
+
+ @Test
+ public void createPreferenceControllers_noSettableDeviceStates_returnsEmptyList() {
+ enableDeviceStateSettableRotationStates(new String[]{}, new String[]{});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).isEmpty();
+ }
+
+ private void enableDeviceStateSettableRotationStates(String[] settableStates,
+ String[] settableStatesDescriptions) {
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ settableStates);
+ when(mResources.getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions)).thenReturn(
+ settableStatesDescriptions);
+ DeviceStateRotationLockSettingsManager.resetInstance();
+ DeviceStateRotationLockSettingsManager.getInstance(mContext)
+ .resetStateForTesting(mResources);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java
new file mode 100644
index 0000000..a5416e7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateOverviewControllerTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
+
+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 = {ShadowRotationPolicy.class, ShadowDeviceStateRotationLockSettingsManager.class})
+public class DeviceStateAutoRotateOverviewControllerTest {
+
+ private final DeviceStateAutoRotateOverviewController mController =
+ new DeviceStateAutoRotateOverviewController(
+ RuntimeEnvironment.application, "device_state_auto_rotate");
+
+ @Test
+ public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationNotSupported_returnsUnsupportedOnDevice() {
+ ShadowRotationPolicy.setRotationSupported(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationNotSupported_returnsUnsupportedOnDevice() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java
new file mode 100644
index 0000000..28a071a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/DeviceStateAutoRotateSettingControllerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2022 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.display;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.search.SearchIndexableRaw;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowRotationPolicy.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+})
+public class DeviceStateAutoRotateSettingControllerTest {
+
+ private static final int DEFAULT_DEVICE_STATE = 1;
+ private static final String DEFAULT_DEVICE_STATE_DESCRIPTION = "Device state description";
+ private static final int DEFAULT_ORDER = -10;
+
+ private final Context mContext = RuntimeEnvironment.application;
+ private final DeviceStateAutoRotateSettingController mController =
+ new DeviceStateAutoRotateSettingController(mContext, DEFAULT_DEVICE_STATE,
+ DEFAULT_DEVICE_STATE_DESCRIPTION, DEFAULT_ORDER);
+ private final DeviceStateRotationLockSettingsManager mAutoRotateSettingsManager =
+ DeviceStateRotationLockSettingsManager.getInstance(mContext);
+
+ @Test
+ public void displayPreference_addsPreferenceToPreferenceScreen() {
+ PreferenceScreen screen = new PreferenceManager(mContext).createPreferenceScreen(mContext);
+
+ mController.displayPreference(screen);
+
+ assertThat(screen.getPreferenceCount()).isEqualTo(1);
+ Preference preference = screen.getPreference(0);
+ assertThat(preference.getTitle().toString()).isEqualTo(DEFAULT_DEVICE_STATE_DESCRIPTION);
+ assertThat(preference.getOrder()).isEqualTo(DEFAULT_ORDER);
+ assertThat(preference.getKey()).isEqualTo(mController.getPreferenceKey());
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationAndDeviceStateRotationEnabled_returnsAvailable() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationDisabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_rotationDisabled_returnsUnsupported() {
+ ShadowRotationPolicy.setRotationSupported(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ int availability = mController.getAvailabilityStatus();
+
+ assertThat(availability).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getPreferenceKey_returnsKeyBasedOnDeviceState() {
+ String key = mController.getPreferenceKey();
+
+ String expectedKey = "auto_rotate_device_state_" + DEFAULT_DEVICE_STATE;
+ assertThat(key).isEqualTo(expectedKey);
+ }
+
+ @Test
+ public void isChecked_settingForStateIsUnlocked_returnsTrue() {
+ mAutoRotateSettingsManager.updateSetting(DEFAULT_DEVICE_STATE, /* rotationLocked= */ false);
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_settingForStateIsLocked_returnsFalse() {
+ mAutoRotateSettingsManager.updateSetting(DEFAULT_DEVICE_STATE, /* rotationLocked= */ true);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_true_deviceStateSettingIsUnlocked() {
+ mController.setChecked(true);
+
+ boolean rotationLocked = mAutoRotateSettingsManager.isRotationLocked(DEFAULT_DEVICE_STATE);
+
+ assertThat(rotationLocked).isFalse();
+ }
+
+ @Test
+ public void setChecked_false_deviceStateSettingIsLocked() {
+ mController.setChecked(false);
+
+ boolean rotationLocked = mAutoRotateSettingsManager.isRotationLocked(DEFAULT_DEVICE_STATE);
+
+ assertThat(rotationLocked).isTrue();
+ }
+
+ @Test
+ public void updateRawDataToIndex_addsItemToList() {
+ List<SearchIndexableRaw> rawData = new ArrayList<>();
+
+ mController.updateRawDataToIndex(rawData);
+
+ assertThat(rawData).hasSize(1);
+ SearchIndexableRaw item = rawData.get(0);
+ assertThat(item.key).isEqualTo(mController.getPreferenceKey());
+ assertThat(item.title).isEqualTo(DEFAULT_DEVICE_STATE_DESCRIPTION);
+ assertThat(item.screenTitle).isEqualTo(mContext.getString(R.string.accelerometer_title));
+ }
+
+ @Test
+ public void getSliceHighlightMenuRes_returnsMenuKeyDisplay() {
+ int sliceHighlightMenuRes = mController.getSliceHighlightMenuRes();
+
+ assertThat(sliceHighlightMenuRes).isEqualTo(R.string.menu_key_display);
+ }
+
+ @Test
+ public void isSliceable_returnsTrue() {
+ assertThat(mController.isSliceable()).isTrue();
+ }
+
+ @Test
+ public void isPublicSlice_returnsTrue() {
+ assertThat(mController.isPublicSlice()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
index 778721a..4fec38b 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotateControllerTest.java
@@ -39,7 +39,10 @@
import androidx.preference.Preference;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import org.junit.Before;
import org.junit.Test;
@@ -63,6 +66,8 @@
@Mock
private Preference mPreference;
private ContentResolver mContentResolver;
+ private final DeviceStateRotationLockSettingsManager mDeviceStateAutoRotateSettingsManager =
+ DeviceStateRotationLockSettingsManager.getInstance(RuntimeEnvironment.application);
@Before
public void setUp() {
@@ -122,6 +127,34 @@
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
+ @Test
+ @Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationLocked_returnDisableDependentSetting() {
+ enableDeviceStateRotation();
+ lockDeviceStateRotation();
+
+ int availabilityStatus = mController.getAvailabilityStatus();
+
+ assertThat(availabilityStatus).isEqualTo(DISABLED_DEPENDENT_SETTING);
+ }
+
+ @Test
+ @Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+ })
+ public void getAvailabilityStatus_deviceStateRotationUnlocked_returnAvailable() {
+ enableDeviceStateRotation();
+ unlockDeviceStateRotation();
+
+ int availabilityStatus = mController.getAvailabilityStatus();
+
+ assertThat(availabilityStatus).isEqualTo(AVAILABLE);
+ }
+
private void enableAutoRotation() {
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 1, UserHandle.USER_CURRENT);
@@ -131,4 +164,23 @@
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT);
}
+
+ private void enableDeviceStateRotation() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+ }
+
+ private void lockDeviceStateRotation() {
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */0, /* rotationLocked= */ true);
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */1, /* rotationLocked= */ true);
+ }
+
+ private void unlockDeviceStateRotation() {
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */0, /* rotationLocked= */ false);
+ mDeviceStateAutoRotateSettingsManager.updateSetting(
+ /* deviceState= */1, /* rotationLocked= */ true);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
index 068de34..39fdb04 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceControllerTest.java
@@ -40,6 +40,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
import org.junit.Before;
@@ -53,7 +54,10 @@
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowSensorPrivacyManager.class)
+@Config(shadows = {
+ ShadowSensorPrivacyManager.class,
+ ShadowDeviceStateRotationLockSettingsManager.class
+})
public class SmartAutoRotatePreferenceControllerTest {
private static final String PACKAGE_NAME = "package_name";
@@ -95,6 +99,7 @@
new SmartAutoRotatePreferenceController(mContext, "smart_auto_rotate"));
when(mController.isCameraLocked()).thenReturn(false);
when(mController.isPowerSaveMode()).thenReturn(false);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
}
@Test
@@ -199,6 +204,16 @@
.UNSUPPORTED_ON_DEVICE);
}
+
+ @Test
+ public void getAvailabilityStatus_deviceStateRotationEnabled_returnsUnsupported() {
+ enableAutoRotationPreference();
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(
+ BasePreferenceController.UNSUPPORTED_ON_DEVICE);
+ }
+
@Test
public void isSliceableCorrectKey_returnsTrue() {
final AutoRotatePreferenceController controller =
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
index 877d2c1..942fed6 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
@@ -18,6 +18,8 @@
import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_ID;
+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.doReturn;
@@ -33,6 +35,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
import android.view.View;
import androidx.preference.Preference;
@@ -40,7 +43,11 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.ResolveInfoBuilder;
+import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
+import com.android.settings.testutils.shadow.ShadowRotationPolicy;
import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import org.junit.Before;
import org.junit.Test;
@@ -49,8 +56,15 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {
+ ShadowDeviceStateRotationLockSettingsManager.class,
+ ShadowRotationPolicy.class
+})
public class SmartAutoRotatePreferenceFragmentTest {
private static final String PACKAGE_NAME = "package_name";
@@ -70,19 +84,24 @@
@Mock
private Preference mRotateSwitchPreference;
+ private Resources mResources;
+ private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- final Context context = spy(RuntimeEnvironment.application);
+ mContext = spy(RuntimeEnvironment.application);
ContentResolver mContentResolver = RuntimeEnvironment.application.getContentResolver();
- when(context.getPackageManager()).thenReturn(mPackageManager);
- when(context.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName();
doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
Manifest.permission.CAMERA, PACKAGE_NAME);
+ mResources = spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
+
final ResolveInfo resolveInfo = new ResolveInfoBuilder(PACKAGE_NAME).build();
resolveInfo.serviceInfo = new ServiceInfo();
when(mPackageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
@@ -90,15 +109,16 @@
mFragment = spy(new SmartAutoRotatePreferenceFragment());
when(mActivity.getPackageManager()).thenReturn(mPackageManager);
when(mFragment.getActivity()).thenReturn(mActivity);
- when(mFragment.getContext()).thenReturn(context);
+ when(mFragment.getContext()).thenReturn(mContext);
doReturn(mView).when(mFragment).getView();
when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID)).thenReturn(
mRotateSwitchPreference);
- mSwitchBar = spy(new SettingsMainSwitchBar(context));
+ mSwitchBar = spy(new SettingsMainSwitchBar(mContext));
when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
}
@@ -111,6 +131,17 @@
}
@Test
+ public void createHeader_deviceStateRotationSupported_switchBarIsDisabled() {
+ ShadowRotationPolicy.setRotationSupported(true);
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(true);
+
+ mFragment.createHeader(mActivity);
+
+ verify(mSwitchBar, never()).show();
+ verify(mRotateSwitchPreference, never()).setVisible(false);
+ }
+
+ @Test
public void createHeader_faceDetectionUnSupported_switchBarIsDisabled() {
doReturn(null).when(mPackageManager).getRotationResolverPackageName();
@@ -120,4 +151,41 @@
verify(mRotateSwitchPreference, never()).setVisible(false);
}
+ @Test
+ public void createPreferenceControllers_noSettableDeviceStates_returnsEmptyList() {
+ enableDeviceStateSettableRotationStates(new String[]{}, new String[]{});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).isEmpty();
+ }
+
+ @Test
+ public void createPreferenceControllers_settableDeviceStates_returnsDeviceStateControllers() {
+ enableDeviceStateSettableRotationStates(new String[]{"0:1", "1:1"},
+ new String[]{"Folded", "Unfolded"});
+
+ List<AbstractPreferenceController> preferenceControllers =
+ mFragment.createPreferenceControllers(mContext);
+
+ assertThat(preferenceControllers).hasSize(2);
+ assertThat(preferenceControllers.get(0)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ assertThat(preferenceControllers.get(1)).isInstanceOf(
+ DeviceStateAutoRotateSettingController.class);
+ }
+
+ private void enableDeviceStateSettableRotationStates(String[] settableStates,
+ String[] settableStatesDescriptions) {
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_perDeviceStateRotationLockDefaults)).thenReturn(
+ settableStates);
+ when(mResources.getStringArray(
+ R.array.config_settableAutoRotationDeviceStatesDescriptions)).thenReturn(
+ settableStatesDescriptions);
+ DeviceStateRotationLockSettingsManager.resetInstance();
+ DeviceStateRotationLockSettingsManager.getInstance(mContext)
+ .resetStateForTesting(mResources);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index 5f08698..d446930 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -85,8 +85,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL);
}
@@ -134,7 +134,7 @@
public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent);
final String batteryStatus =
- Utils.getBatteryStatus(mContext, mChargingIntent);
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false);
mBatteryBroadcastReceiver.mBatteryLevel = batteryLevel;
mBatteryBroadcastReceiver.mBatteryStatus = batteryStatus;
@@ -159,8 +159,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
.isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN);
// 2 times because register will force update the battery
diff --git a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
index bf4811b..68d80d4 100644
--- a/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/EthernetTetherPreferenceControllerTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -65,10 +66,9 @@
mPreference = spy(SwitchPreference.class);
when(mContext.getSystemService(Context.TETHERING_SERVICE)).thenReturn(mTetheringManager);
when(mTetheringManager.getTetherableIfaces()).thenReturn(new String[]{ETHERNET_REGEX});
- when(mContext.getSystemService(Context.ETHERNET_SERVICE)).thenReturn(mEthernetManager);
+ when(mContext.getSystemService(EthernetManager.class)).thenReturn(mEthernetManager);
mController = new EthernetTetherPreferenceController(mContext, "ethernet");
mController.setTetherEnabler(mTetherEnabler);
- ReflectionHelpers.setField(mController, "mEthernetRegex", ETHERNET_REGEX);
ReflectionHelpers.setField(mController, "mPreference", mPreference);
}
@@ -77,7 +77,8 @@
public void lifecycle_shouldRegisterReceiverOnStart() {
mController.onStart();
- verify(mEthernetManager).addListener(eq(mController.mEthernetListener));
+ verify(mEthernetManager).addInterfaceStateListener(any(),
+ eq(mController.mEthernetListener));
}
@Test
@@ -95,11 +96,10 @@
@Test
public void lifecycle_shouldUnregisterReceiverOnStop() {
mController.onStart();
- EthernetManager.Listener listener = mController.mEthernetListener;
+ EthernetManager.InterfaceStateListener listener = mController.mEthernetListener;
mController.onStop();
- verify(mEthernetManager).removeListener(eq(listener));
- assertThat(mController.mEthernetListener).isNull();
+ verify(mEthernetManager).removeInterfaceStateListener(eq(listener));
}
@Test
@@ -110,8 +110,11 @@
@Test
public void shouldShow_noEthernetInterface() {
- ReflectionHelpers.setField(mController, "mEthernetRegex", "");
- assertThat(mController.shouldShow()).isFalse();
+ when(mContext.getSystemService(EthernetManager.class)).thenReturn(null);
+
+ final EthernetTetherPreferenceController controller =
+ new EthernetTetherPreferenceController(mContext, "ethernet");
+ assertThat(controller.shouldShow()).isFalse();
}
@Test
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index b73e1c9..b04a2cd 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -139,7 +139,7 @@
mPackageManager = Shadow.extract(mContext.getPackageManager());
mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true);
- mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
diff --git a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
index 3910906..2580bfd 100644
--- a/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/HandsFreeProfileOutputPreferenceControllerTest.java
@@ -130,7 +130,7 @@
when(mLocalBluetoothProfileManager.getHeadsetProfile()).thenReturn(mHeadsetProfile);
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index 306c4c1..10471cb 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -171,7 +171,7 @@
when(mLocalBluetoothProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
- mBluetoothManager = new BluetoothManager(mContext);
+ mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_1));
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
new file mode 100644
index 0000000..72df3cc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowDeviceStateRotationLockSettingsManager.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.testutils.shadow;
+
+import android.content.Context;
+
+import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@Implements(DeviceStateRotationLockSettingsManager.class)
+public class ShadowDeviceStateRotationLockSettingsManager {
+
+ private static boolean sDeviceStateRotationLockEnabled;
+
+ @Implementation
+ public static boolean isDeviceStateRotationLockEnabled(Context context) {
+ return sDeviceStateRotationLockEnabled;
+ }
+
+ public static void setDeviceStateRotationLockEnabled(boolean enabled) {
+ sDeviceStateRotationLockEnabled = enabled;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index 2c694ac..0fa3d5c 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -194,6 +194,29 @@
}
@Test
+ public void testAssignDefaultPhoto_hasDefaultUserIconSize() {
+ doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
+ int size = 100;
+ try {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.dimen.user_icon_size,
+ size);
+ assertThat(UserSettings.assignDefaultPhoto(mContext, ACTIVE_USER_ID)).isTrue();
+
+ int pxSize = mContext.getResources()
+ .getDimensionPixelSize(com.android.internal.R.dimen.user_icon_size);
+
+ ArgumentCaptor<Bitmap> captor = ArgumentCaptor.forClass(Bitmap.class);
+ verify(mUserManager).setUserIcon(eq(ACTIVE_USER_ID), captor.capture());
+ Bitmap bitmap = captor.getValue();
+ assertThat(bitmap.getWidth()).isEqualTo(pxSize);
+ assertThat(bitmap.getHeight()).isEqualTo(pxSize);
+ } finally {
+ SettingsShadowResources.reset();
+ }
+ }
+
+ @Test
public void testExitGuest_ShouldLogAction() {
mUserCapabilities.mIsGuest = true;
mFragment.exitGuest();
diff --git a/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
new file mode 100644
index 0000000..3e6ac09
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 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.biometrics;
+
+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.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+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 BiometricNavigationUtilsTest {
+
+ private static final String SETTINGS_CLASS_NAME = "SettingsClassName";
+ private static final String EXTRA_KEY = "EXTRA_KEY";
+
+ @Mock
+ private UserManager mUserManager;
+ private Context mContext;
+ private BiometricNavigationUtils mBiometricNavigationUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(ApplicationProvider.getApplicationContext());
+ when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
+ doNothing().when(mContext).startActivity(any());
+ mBiometricNavigationUtils = new BiometricNavigationUtils();
+ }
+
+ @Test
+ public void openBiometricSettings_quietMode_launchesQuiteModeDialog() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
+ Bundle.EMPTY);
+
+ assertQuietModeDialogLaunchRequested();
+ }
+
+ @Test
+ public void openBiometricSettings_quietMode_returnsFalse() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isFalse();
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_emptyExtras_launchesFragmentWithoutExtras() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY);
+
+ assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_emptyExtras_returnsTrue() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isTrue();
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_withExtras_launchesFragmentWithExtras() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ final Bundle extras = createNotEmptyExtras();
+ mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME, extras);
+
+ assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
+ }
+
+ @Test
+ public void openBiometricSettings_noQuietMode_withExtras_returnsTrue() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ assertThat(mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras())).isTrue();
+ }
+
+ private Bundle createNotEmptyExtras() {
+ final Bundle bundle = new Bundle();
+ bundle.putInt(EXTRA_KEY, 0);
+ return bundle;
+ }
+
+ private void assertQuietModeDialogLaunchRequested() {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(intentCaptor.capture());
+
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getComponent().getPackageName())
+ .isEqualTo("android");
+ assertThat(intent.getComponent().getClassName())
+ .isEqualTo("com.android.internal.app.UnlaunchableAppActivity");
+ }
+
+ private void assertSettingsPageLaunchRequested(boolean shouldContainExtras) {
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(intentCaptor.capture());
+
+ Intent intent = intentCaptor.getValue();
+ assertThat(intent.getComponent().getPackageName())
+ .isEqualTo("com.android.settings");
+ assertThat(intent.getComponent().getClassName())
+ .isEqualTo(SETTINGS_CLASS_NAME);
+ assertThat(intent.getExtras().containsKey(EXTRA_KEY)).isEqualTo(shouldContainExtras);
+ }
+
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
new file mode 100644
index 0000000..55b3fae
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtilsTest.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2022 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.biometrics.combination;
+
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE;
+import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Settings;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class CombinedBiometricStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private CombinedBiometricStatusUtils mCombinedBiometricStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mCombinedBiometricStatusUtils = new CombinedBiometricStatusUtils(mApplicationContext);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_whenFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_whenFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_whenFingerprint_whenFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mCombinedBiometricStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_whenFaceDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FACE | KEYGUARD_DISABLE_FINGERPRINT);
+
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mCombinedBiometricStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_whenFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FINGERPRINT);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintEnabled_whenFaceDisabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(KEYGUARD_DISABLE_FACE);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintEnabled_whenFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mCombinedBiometricStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenMultipleFingerprints_returnsBothFpMultiple() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(2));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_both_fp_multiple"));
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenSingleFingerprint_returnsBothFpSingle() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(1));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_both_fp_single"));
+ }
+
+ @Test
+ public void getSummary_whenFaceEnrolled_whenNoFingerprints_returnsFace() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(0));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary"));
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenMultipleFingerprints_returnsFingerprints() {
+ final int enrolledFingerprintsCount = 2;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenSingleFingerprints_returnsFingerprints() {
+ final int enrolledFingerprintsCount = 1;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSummary_whenNoFaceEnrolled_whenNoFingerprints_returnsNoneEnrolled() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt()))
+ .thenReturn(createFingerprintList(0));
+
+ assertThat(mCombinedBiometricStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_biometric_preference_summary_none_enrolled"));
+ }
+
+ @Test
+ public void getSettingsClassName_returnsCombinedBiometricSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.getSettingsClassName())
+ .isEqualTo(Settings.CombinedBiometricSettingsActivity.class.getName());
+ }
+
+ @Test
+ public void getProfileSettingsClassName_returnsCombinedBiometricProfileSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mCombinedBiometricStatusUtils.getProfileSettingsClassName())
+ .isEqualTo(Settings.CombinedBiometricProfileSettingsActivity.class.getName());
+ }
+
+ private List<Fingerprint> createFingerprintList(int size) {
+ final List<Fingerprint> fingerprintList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
+ }
+ return fingerprintList;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
new file mode 100644
index 0000000..f670fad
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/face/FaceStatusUtilsTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 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.biometrics.face;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.Settings;
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class FaceStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private FaceStatusUtils mFaceStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mFaceStatusUtils = new FaceStatusUtils(mApplicationContext, mFaceManager);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFaceDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FACE);
+
+ final RestrictedLockUtils.EnforcedAdmin admin = mFaceStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_withFaceEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mFaceStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenNotEnrolled_returnsSummaryNone() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary_none"));
+ }
+
+ @Test
+ public void getSummary_whenEnrolled_returnsSummary() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_face_preference_summary"));
+ }
+
+ @Test
+ public void getSettingsClassName_whenNotEnrolled_returnsFaceEnrollInduction() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFaceStatusUtils.getSettingsClassName())
+ .isEqualTo(FaceEnrollIntroduction.class.getName());
+ }
+
+ @Test
+ public void getSettingsClassName_whenEnrolled_returnsFaceSettings() {
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+
+ assertThat(mFaceStatusUtils.getSettingsClassName())
+ .isEqualTo(Settings.FaceSettingsActivity.class.getName());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
new file mode 100644
index 0000000..b4abb5d
--- /dev/null
+++ b/tests/unit/src/com/android/settings/biometrics/fingerprint/FingerprintStatusUtilsTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 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.biometrics.fingerprint;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.testutils.ResourcesUtils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class FingerprintStatusUtilsTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("package", "class");
+ private static final UserHandle USER_HANDLE = new UserHandle(UserHandle.myUserId());
+
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Mock
+ private FaceManager mFaceManager;
+
+ private Context mApplicationContext;
+ private FingerprintStatusUtils mFingerprintStatusUtils;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mApplicationContext = spy(ApplicationProvider.getApplicationContext());
+ when(mApplicationContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mDevicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(USER_HANDLE))
+ .thenReturn(COMPONENT_NAME);
+ when(mApplicationContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mApplicationContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .thenReturn(mDevicePolicyManager);
+ when(mApplicationContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ mFingerprintStatusUtils =
+ new FingerprintStatusUtils(mApplicationContext, mFingerprintManager);
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withoutFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withoutFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withoutFace_returnsTrue() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_withFingerprint_withFace_returnsFalse() {
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getDisabledAdmin_whenFingerprintDisabled_returnsEnforcedAdmin() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+
+ final RestrictedLockUtils.EnforcedAdmin admin =
+ mFingerprintStatusUtils.getDisablingAdmin();
+
+ assertThat(admin).isEqualTo(new RestrictedLockUtils.EnforcedAdmin(
+ COMPONENT_NAME, UserManager.DISALLOW_BIOMETRIC, USER_HANDLE));
+ }
+
+ @Test
+ public void getDisabledAdmin_withFingerprintEnabled_returnsNull() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(COMPONENT_NAME)).thenReturn(0);
+
+ assertThat(mFingerprintStatusUtils.getDisablingAdmin()).isNull();
+ }
+
+ @Test
+ public void getSummary_whenNotEnrolled_returnsSummaryNone() {
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.getSummary())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "security_settings_fingerprint_preference_summary_none"));
+ }
+
+ @Test
+ public void getSummary_whenEnrolled_returnsSummary() {
+ final int enrolledFingerprintsCount = 2;
+ final int stringResId = ResourcesUtils.getResourcesId(
+ ApplicationProvider.getApplicationContext(), "plurals",
+ "security_settings_fingerprint_preference_summary");
+ final String summary = mApplicationContext.getResources().getQuantityString(
+ stringResId, enrolledFingerprintsCount /* quantity */,
+ enrolledFingerprintsCount /* formatArgs */);
+
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+ when(mFingerprintManager.getEnrolledFingerprints(anyInt())).thenReturn(
+ createFingerprintList(enrolledFingerprintsCount));
+
+ assertThat(mFingerprintStatusUtils.getSummary()).isEqualTo(summary);
+ }
+
+ @Test
+ public void getSettingsClassName_whenNotEnrolled_returnsFingerprintEnrollInduction() {
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(false);
+
+ assertThat(mFingerprintStatusUtils.getSettingsClassName())
+ .isEqualTo(FingerprintEnrollIntroduction.class.getName());
+ }
+
+ @Test
+ public void getSettingsClassName_whenEnrolled_returnsFingerprintSettings() {
+ when(mFingerprintManager.hasEnrolledFingerprints(anyInt())).thenReturn(true);
+
+ assertThat(mFingerprintStatusUtils.getSettingsClassName())
+ .isEqualTo(FingerprintSettings.class.getName());
+ }
+
+ private List<Fingerprint> createFingerprintList(int size) {
+ final List<Fingerprint> fingerprintList = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ fingerprintList.add(new Fingerprint("fingerprint" + i, 0, 0));
+ }
+ return fingerprintList;
+ }
+}
diff --git a/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java b/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
index d4df97f..1cfee0f 100644
--- a/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
+++ b/tests/unit/src/com/android/settings/privacy/PrivacyDashboardActivityTest.java
@@ -33,7 +33,7 @@
import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import org.junit.Before;
import org.junit.Test;
@@ -48,14 +48,14 @@
private static final String DEFAULT_FRAGMENT_CLASSNAME = "DefaultFragmentClassname";
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
private Settings.PrivacyDashboardActivity mActivity;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
final Intent intent = new Intent();
intent.setAction(android.provider.Settings.ACTION_PRIVACY_SETTINGS);
intent.setClass(InstrumentationRegistry.getInstrumentation().getTargetContext(),
@@ -78,7 +78,7 @@
@Test
public void onCreate_whenSafetyCenterEnabled_redirectsToSafetyCenter() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mActivity.handleSafetyCenterRedirection();
@@ -89,7 +89,7 @@
@Test
public void onCreate_whenSafetyCenterDisabled_doesntRedirectToSafetyCenter() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
mActivity.handleSafetyCenterRedirection();
verify(mActivity, times(0)).startActivity(any());
diff --git a/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java
index 495700a..bc6be2e 100644
--- a/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/privacy/TopLevelPrivacyEntryPreferenceControllerTest.java
@@ -26,7 +26,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.security.TopLevelSecurityEntryPreferenceController;
import org.junit.Before;
@@ -43,12 +43,12 @@
private TopLevelPrivacyEntryPreferenceController mTopLevelPrivacyEntryPreferenceController;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
mTopLevelPrivacyEntryPreferenceController =
new TopLevelPrivacyEntryPreferenceController(
@@ -57,7 +57,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
@@ -65,7 +65,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
assertThat(mTopLevelPrivacyEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);
diff --git a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
index f53d336..2627d24 100644
--- a/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/BiometricsSafetySourceTest.java
@@ -41,26 +41,21 @@
@Mock
private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
- @Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = ApplicationProvider.getApplicationContext();
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
}
@After
public void tearDown() {
SafetyCenterManagerWrapper.sInstance = null;
- SafetyCenterStatusHolder.sInstance = null;
}
@Test
public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
BiometricsSafetySource.sendSafetyData(mApplicationContext);
@@ -70,7 +65,7 @@
@Test
// TODO(b/215517420): Adapt this test when method is implemented.
public void sendSafetyData_whenSafetyCenterIsEnabled_sendsNoData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
BiometricsSafetySource.sendSafetyData(mApplicationContext);
diff --git a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
index 052f981..64b9692 100644
--- a/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/LockScreenSafetySourceTest.java
@@ -19,19 +19,22 @@
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.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.safetycenter.SafetySourceData;
import android.safetycenter.SafetySourceStatus;
+import android.safetycenter.SafetySourceStatus.IconAction;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.settings.SettingsActivity;
-import com.android.settings.password.ChooseLockGeneric;
+import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
+import com.android.settings.testutils.ResourcesUtils;
import org.junit.After;
import org.junit.Before;
@@ -44,56 +47,188 @@
@RunWith(AndroidJUnit4.class)
public class LockScreenSafetySourceTest {
+ private static final String SUMMARY = "summary";
+ private static final String FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT = "choose_lock_generic";
+ private static final String FAKE_ACTION_SCREEN_LOCK_SETTINGS = "screen_lock_settings";
+
private Context mApplicationContext;
@Mock
private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private ScreenLockPreferenceDetailsUtils mScreenLockPreferenceDetailsUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = ApplicationProvider.getApplicationContext();
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
}
@After
public void tearDown() {
SafetyCenterManagerWrapper.sInstance = null;
- SafetyCenterStatusHolder.sInstance = null;
}
@Test
public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
- LockScreenSafetySource.sendSafetyData(mApplicationContext);
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
}
@Test
- public void sendSafetyData_whenSafetyCenterIsEnabled_sendsPlaceholderData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ public void sendSafetyData_whenScreenLockIsDisabled_sendsNoData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(false);
- LockScreenSafetySource.sendSafetyData(mApplicationContext);
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
+ public void sendSafetyData_whenScreenLockIsEnabled_sendsData() {
+ whenScreenLockIsEnabled();
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
SafetySourceData safetySourceData = captor.getValue();
SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
assertThat(safetySourceData.getId()).isEqualTo(LockScreenSafetySource.SAFETY_SOURCE_ID);
- assertThat(safetySourceStatus.getTitle().toString()).isEqualTo("Lock Screen");
+ assertThat(safetySourceStatus.getTitle().toString())
+ .isEqualTo(ResourcesUtils.getResourcesString(
+ mApplicationContext,
+ "unlock_set_unlock_launch_picker_title"));
assertThat(safetySourceStatus.getSummary().toString())
- .isEqualTo("Lock screen settings");
+ .isEqualTo(SUMMARY);
+ assertThat(safetySourceStatus.getPendingIntent().getIntent()).isNotNull();
+ assertThat(safetySourceStatus.getPendingIntent().getIntent().getAction())
+ .isEqualTo(FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT);
+ }
+
+ @Test
+ public void sendSafetyData_whenLockPatternIsSecure_sendsStatusLevelOk() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
assertThat(safetySourceStatus.getStatusLevel())
.isEqualTo(SafetySourceStatus.STATUS_LEVEL_OK);
- assertThat(safetySourceStatus.getPendingIntent()).isNotNull();
- assertThat(safetySourceStatus.getPendingIntent().getIntent().getStringExtra(
- SettingsActivity.EXTRA_SHOW_FRAGMENT))
- .isEqualTo(ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
+ }
+
+ @Test
+ public void sendSafetyData_whenLockPatternIsNotSecure_sendsStatusLevelRecommendation() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.getStatusLevel())
+ .isEqualTo(SafetySourceStatus.STATUS_LEVEL_RECOMMENDATION);
+ }
+
+ @Test
+ public void sendSafetyData_whenPasswordQualityIsManaged_sendsDisabled() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
+ .thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void sendSafetyData_whenPasswordQualityIsNotManaged_sendsEnabled() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.isPasswordQualityManaged(anyInt(), any()))
+ .thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.isEnabled()).isTrue();
+ }
+
+ @Test
+ public void sendSafetyData_whenShouldShowGearMenu_sendsGearMenuActionIcon() {
+ whenScreenLockIsEnabled();
+ final Intent launchScreenLockSettings = new Intent(FAKE_ACTION_SCREEN_LOCK_SETTINGS);
+ when(mScreenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent())
+ .thenReturn(launchScreenLockSettings);
+ when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(true);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ final ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(
+ SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ final IconAction iconAction = captor.getValue().getStatus().getIconAction();
+
+ assertThat(iconAction.getIconType()).isEqualTo(IconAction.ICON_TYPE_GEAR);
+ assertThat(iconAction.getPendingIntent().getIntent().getAction())
+ .isEqualTo(FAKE_ACTION_SCREEN_LOCK_SETTINGS);
+ }
+
+ @Test
+ public void sendSafetyData_whenShouldNotShowGearMenu_sendsNoGearMenuActionIcon() {
+ whenScreenLockIsEnabled();
+ when(mScreenLockPreferenceDetailsUtils.shouldShowGearMenu()).thenReturn(false);
+
+ LockScreenSafetySource.sendSafetyData(mApplicationContext,
+ mScreenLockPreferenceDetailsUtils);
+
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper).sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+ SafetySourceStatus safetySourceStatus = safetySourceData.getStatus();
+
+ assertThat(safetySourceStatus.getIconAction()).isNull();
+ }
+
+ private void whenScreenLockIsEnabled() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.isAvailable()).thenReturn(true);
+ when(mScreenLockPreferenceDetailsUtils.getSummary(anyInt())).thenReturn(SUMMARY);
+
+ Intent launchChooseLockGenericFragment = new Intent(
+ FAKE_ACTION_CHOOSE_LOCK_GENERIC_FRAGMENT);
+ when(mScreenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent())
+ .thenReturn(launchChooseLockGenericFragment);
}
}
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusHolderTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java
similarity index 87%
rename from tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusHolderTest.java
rename to tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java
index 2a31eb8..432a5d6 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetyCenterStatusHolderTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetyCenterManagerWrapperTest.java
@@ -32,7 +32,7 @@
import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
-public class SafetyCenterStatusHolderTest {
+public class SafetyCenterManagerWrapperTest {
@Mock
private Context mContext;
@@ -44,13 +44,13 @@
@Test
public void isEnabled_whenContextNull_returnsFalse() {
- assertThat(SafetyCenterStatusHolder.get().isEnabled(null)).isFalse();
+ assertThat(SafetyCenterManagerWrapper.get().isEnabled(null)).isFalse();
}
@Test
public void isEnabled_whenSystemServiceNull_returnsFalse() {
when(mContext.getSystemService(SafetyCenterManager.class)).thenReturn(null);
- assertThat(SafetyCenterStatusHolder.get().isEnabled(mContext)).isFalse();
+ assertThat(SafetyCenterManagerWrapper.get().isEnabled(mContext)).isFalse();
}
}
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index 581286b..f2a28ff 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -16,6 +16,7 @@
package com.android.settings.safetycenter;
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
import static com.google.common.truth.Truth.assertThat;
@@ -33,6 +34,9 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.testutils.FakeFeatureFactory;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -50,38 +54,26 @@
private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private LockPatternUtils mLockPatternUtils;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mApplicationContext = ApplicationProvider.getApplicationContext();
+ final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+ when(featureFactory.securityFeatureProvider.getLockPatternUtils(mApplicationContext))
+ .thenReturn(mLockPatternUtils);
SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
}
@After
public void tearDown() {
SafetyCenterManagerWrapper.sInstance = null;
- SafetyCenterStatusHolder.sInstance = null;
}
@Test
- public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(false);
- Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
-
- new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
-
- verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
- }
-
- @Test
- public void sendSafetyData_whenSafetyCenterIsEnabled_withNoSourceIds_sendsNoData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ public void sendSafetyData_whenSafetyCenterIsEnabled_withNoIntentAction_sendsNoData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
Intent intent = new Intent().putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, new String[]{});
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
@@ -90,12 +82,52 @@
}
@Test
- public void sendSafetyData_withLockscreenSourceId_sendsLockscreenData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(false);
Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
+
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
+ public void sendSafetyData_whenSafetyCenterIsEnabled_withNullSourceIds_sendsNoData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent = new Intent().setAction(ACTION_REFRESH_SAFETY_SOURCES);
+
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
+ public void sendSafetyData_whenSafetyCenterIsEnabled_withNoSourceIds_sendsNoData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent =
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, new String[]{});
+
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
+ public void sendSafetyData_withLockscreenSourceId_sendsLockscreenData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent =
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
@@ -108,15 +140,33 @@
@Test
public void sendSafetyData_withBiometricsSourceId_sendsBiometricData() {
- when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ BiometricsSafetySource.SAFETY_SOURCE_ID });
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ BiometricsSafetySource.SAFETY_SOURCE_ID });
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
// TODO(b/215517420): Update this test when BiometricSafetySource is implemented.
verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
}
+
+ @Test
+ public void sendSafetyData_onBootCompleted_sendsBiometricAndLockscreenData() {
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent = new Intent().setAction(Intent.ACTION_BOOT_COMPLETED);
+
+ // TODO(b/215517420): Update this test when BiometricSafetySource is implemented to test
+ // that biometrics data is also sent.
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper, times(1))
+ .sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+
+ assertThat(safetySourceData.getId()).isEqualTo(LockScreenSafetySource.SAFETY_SOURCE_ID);
+ }
}
diff --git a/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java
index e9c451e..25cd774 100644
--- a/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/TopLevelSafetyCenterEntryPreferenceControllerTest.java
@@ -32,6 +32,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +50,7 @@
private Preference mPreference;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Mock
private Context mContext;
@@ -57,7 +58,7 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
mPreference = new Preference(ApplicationProvider.getApplicationContext());
mPreference.setKey(PREFERENCE_KEY);
@@ -67,6 +68,11 @@
new TopLevelSafetyCenterEntryPreferenceController(mContext, PREFERENCE_KEY);
}
+ @After
+ public void tearDown() {
+ SafetyCenterManagerWrapper.sInstance = null;
+ }
+
@Test
public void handlePreferenceTreeClick_forDifferentPreferenceKey_isNotHandled() {
Preference preference = new Preference(ApplicationProvider.getApplicationContext());
@@ -104,7 +110,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsUnavailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSafetyCenterEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
@@ -112,7 +118,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsAvailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
assertThat(mTopLevelSafetyCenterEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSafetyCenterEntryPreferenceController.AVAILABLE);
diff --git a/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java b/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
index 3d13b48..f471128 100644
--- a/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
+++ b/tests/unit/src/com/android/settings/security/ScreenLockPreferenceDetailsUtilsTest.java
@@ -85,7 +85,7 @@
.thenReturn(mDevicePolicyManager);
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(mContext).startActivity(any());
- when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[] {});
+ when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
when(featureFactory.securityFeatureProvider.getLockPatternUtils(mContext))
@@ -254,6 +254,20 @@
}
@Test
+ public void isLockPatternSecure_patternIsSecure_shouldReturnTrue() {
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
+
+ assertThat(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).isTrue();
+ }
+
+ @Test
+ public void isLockPatternSecure_patternIsNotSecure_shouldReturnFalse() {
+ when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false);
+
+ assertThat(mScreenLockPreferenceDetailsUtils.isLockPatternSecure()).isFalse();
+ }
+
+ @Test
public void shouldShowGearMenu_patternIsSecure_shouldReturnTrue() {
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
@@ -275,6 +289,13 @@
}
@Test
+ public void getLaunchScreenLockSettingsIntent_returnsIntent() {
+ final Intent intent = mScreenLockPreferenceDetailsUtils.getLaunchScreenLockSettingsIntent();
+
+ assertFragmentLaunchIntent(intent, ScreenLockSettings.class.getName());
+ }
+
+ @Test
public void openChooseLockGenericFragment_noQuietMode_shouldSendIntent_shouldReturnTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
@@ -282,6 +303,17 @@
assertFragmentLaunchRequested(ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
}
+ @Test
+ public void getLaunchChooseLockGenericFragmentIntent_noQuietMode_returnsIntent() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ final Intent intent =
+ mScreenLockPreferenceDetailsUtils.getLaunchChooseLockGenericFragmentIntent();
+
+ assertFragmentLaunchIntent(intent,
+ ChooseLockGeneric.ChooseLockGenericFragment.class.getName());
+ }
+
private void whenConfigShowUnlockSetOrChangeIsEnabled(boolean enabled) {
final int resId = ResourcesUtils.getResourcesId(
ApplicationProvider.getApplicationContext(), "bool",
@@ -301,6 +333,10 @@
verify(mContext).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
+ assertFragmentLaunchIntent(intent, fragmentClassName);
+ }
+
+ private void assertFragmentLaunchIntent(Intent intent, String fragmentClassName) {
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(fragmentClassName);
assertThat(intent.getIntExtra(
diff --git a/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java b/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
index ac91abb..2efb357 100644
--- a/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
+++ b/tests/unit/src/com/android/settings/security/SecurityAdvancedSettingsTest.java
@@ -29,7 +29,7 @@
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.ResourcesUtils;
import com.android.settingslib.drawer.CategoryKey;
@@ -51,7 +51,7 @@
private SecurityAdvancedSettings mSecurityAdvancedSettings;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Before
@UiThreadTest
@@ -61,7 +61,7 @@
Looper.prepare();
}
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
mContext = ApplicationProvider.getApplicationContext();
mSecurityAdvancedSettings = spy(new SecurityAdvancedSettings());
@@ -76,7 +76,7 @@
@Test
public void getCategoryKey_whenSafetyCenterIsEnabled_returnsSecurity() {
- when(mSafetyCenterStatusHolder.isEnabled(any())).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any())).thenReturn(true);
assertThat(mSecurityAdvancedSettings.getCategoryKey())
.isEqualTo(CategoryKey.CATEGORY_SECURITY_ADVANCED_SETTINGS);
@@ -84,7 +84,7 @@
@Test
public void getCategoryKey_whenAlternativeFragmentPresented_returnsAlternative() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
setupAlternativeFragment(true, ALTERNATIVE_CATEGORY_KEY);
assertThat(mSecurityAdvancedSettings.getCategoryKey())
@@ -93,7 +93,7 @@
@Test
public void getCategoryKey_whenNoAlternativeFragmentPresented_returnsLegacy() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
setupAlternativeFragment(false, null);
assertThat(mSecurityAdvancedSettings.getCategoryKey())
diff --git a/tests/unit/src/com/android/settings/security/SecurityDashboardActivityTest.java b/tests/unit/src/com/android/settings/security/SecurityDashboardActivityTest.java
index 3348663..82112c9 100644
--- a/tests/unit/src/com/android/settings/security/SecurityDashboardActivityTest.java
+++ b/tests/unit/src/com/android/settings/security/SecurityDashboardActivityTest.java
@@ -33,7 +33,7 @@
import com.android.settings.Settings;
import com.android.settings.SettingsActivity;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -53,14 +53,14 @@
private Intent mDefaultIntent;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
FakeFeatureFactory mFeatureFactory = FakeFeatureFactory.setupForTest();
mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider();
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
mDefaultIntent = new Intent();
mDefaultIntent.setAction(android.provider.Settings.ACTION_SECURITY_SETTINGS);
@@ -122,7 +122,7 @@
@Test
public void onCreate_whenSafetyCenterEnabled_redirectsToSafetyCenter() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mActivity.handleSafetyCenterRedirection();
@@ -133,7 +133,7 @@
@Test
public void onCreate_whenSafetyCenterDisabled_doesntRedirectToSafetyCenter() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
mActivity.handleSafetyCenterRedirection();
diff --git a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
index 81ebda6..eb8fa17 100644
--- a/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/security/TopLevelSecurityEntryPreferenceControllerTest.java
@@ -31,7 +31,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.SettingsActivity;
-import com.android.settings.safetycenter.SafetyCenterStatusHolder;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -55,14 +55,14 @@
@Mock
private Context mContext;
@Mock
- private SafetyCenterStatusHolder mSafetyCenterStatusHolder;
+ private SafetyCenterManagerWrapper mSafetyCenterManagerWrapper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSecuritySettingsFeatureProvider = mFeatureFactory.getSecuritySettingsFeatureProvider();
- SafetyCenterStatusHolder.sInstance = mSafetyCenterStatusHolder;
+ SafetyCenterManagerWrapper.sInstance = mSafetyCenterManagerWrapper;
mPreference = new Preference(ApplicationProvider.getApplicationContext());
mPreference.setKey(PREFERENCE_KEY);
@@ -128,7 +128,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterEnabled_returnsUnavailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(true);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(true);
assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.CONDITIONALLY_UNAVAILABLE);
@@ -136,7 +136,7 @@
@Test
public void getAvailabilityStatus_whenSafetyCenterDisabled_returnsAvailable() {
- when(mSafetyCenterStatusHolder.isEnabled(any(Context.class))).thenReturn(false);
+ when(mSafetyCenterManagerWrapper.isEnabled(any(Context.class))).thenReturn(false);
assertThat(mTopLevelSecurityEntryPreferenceController.getAvailabilityStatus())
.isEqualTo(TopLevelSecurityEntryPreferenceController.AVAILABLE);