Merge "Revert submission"
diff --git a/res/layout/bluetooth_audio_channel_mode_dialog.xml b/res/layout/bluetooth_audio_channel_mode_dialog.xml
new file mode 100644
index 0000000..9154ee4
--- /dev/null
+++ b/res/layout/bluetooth_audio_channel_mode_dialog.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="8dp">
+
+ <RadioGroup
+ android:id="@+id/bluetooth_audio_channel_mode_radio_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/developer_option_dialog_margin_start">
+
+ <include
+ android:id="@+id/bluetooth_audio_channel_mode_default"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_channel_mode_mono"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_channel_mode_stereo"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+ </RadioGroup>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_help_info"
+ layout="@layout/preference_widget_dialog_summary"/>
+
+ </LinearLayout>
+
+</ScrollView>
+
diff --git a/res/layout/bluetooth_audio_codec_dialog.xml b/res/layout/bluetooth_audio_codec_dialog.xml
new file mode 100644
index 0000000..9a6b768
--- /dev/null
+++ b/res/layout/bluetooth_audio_codec_dialog.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="8dp">
+
+ <RadioGroup
+ android:id="@+id/bluetooth_audio_codec_radio_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/developer_option_dialog_margin_start">
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_default"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_sbc"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_aac"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_aptx"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_aptx_hd"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_ldac"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_enable_optional"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_disable_optional"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+ </RadioGroup>
+
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_help_info"
+ layout="@layout/preference_widget_dialog_summary"/>
+
+ </LinearLayout>
+
+</ScrollView>
+
diff --git a/res/layout/bluetooth_audio_sample_rate_dialog.xml b/res/layout/bluetooth_audio_sample_rate_dialog.xml
new file mode 100644
index 0000000..03bbe48
--- /dev/null
+++ b/res/layout/bluetooth_audio_sample_rate_dialog.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="8dp">
+
+ <RadioGroup
+ android:id="@+id/bluetooth_audio_sample_rate_radio_group"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/developer_option_dialog_margin_start">
+
+ <include
+ android:id="@+id/bluetooth_audio_sample_rate_default"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_sample_rate_441"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_sample_rate_480"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_sample_rate_882"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+
+ <include
+ android:id="@+id/bluetooth_audio_sample_rate_960"
+ layout="@layout/preference_widget_dialog_radiobutton"/>
+ </RadioGroup>
+
+ <include
+ android:id="@+id/bluetooth_audio_codec_help_info"
+ layout="@layout/preference_widget_dialog_summary"/>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/dark_ui_activation_button.xml b/res/layout/dark_ui_activation_button.xml
new file mode 100644
index 0000000..5f9eefc
--- /dev/null
+++ b/res/layout/dark_ui_activation_button.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight">
+
+ <Button
+ android:id="@+id/dark_ui_turn_on_button"
+ style="@style/ActionPrimaryButton"
+ android:layout_marginStart="@dimen/screen_margin_sides"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ <Button
+ android:id="@+id/dark_ui_turn_off_button"
+ style="@style/ActionSecondaryButton"
+ android:layout_marginStart="@dimen/screen_margin_sides"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/xml/inactive_apps.xml b/res/layout/preference_widget_dialog_radiobutton.xml
similarity index 67%
copy from res/xml/inactive_apps.xml
copy to res/layout/preference_widget_dialog_radiobutton.xml
index 6f93bdb..1cebd1b 100644
--- a/res/xml/inactive_apps.xml
+++ b/res/layout/preference_widget_dialog_radiobutton.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,9 +14,10 @@
limitations under the License.
-->
-<PreferenceScreen
+<!-- Layout used by BaseBluetoothDialogPreference for the radio button in developer option dialog. -->
+
+<RadioButton
xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/inactive_apps_title">
-
-</PreferenceScreen>
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/developer_option_dialog_min_height"/>
diff --git a/res/layout/preference_widget_dialog_summary.xml b/res/layout/preference_widget_dialog_summary.xml
new file mode 100644
index 0000000..2978efb
--- /dev/null
+++ b/res/layout/preference_widget_dialog_summary.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Layout used by BaseBluetoothDialogPreference for the summary in developer option dialog. -->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:text="@string/bluetooth_select_a2dp_codec_type_help_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/developer_option_dialog_margin_top"
+ android:minHeight="@dimen/developer_option_dialog_min_height"
+ android:paddingStart="@dimen/developer_option_dialog_padding_start"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
diff --git a/res/layout/private_dns_mode_dialog.xml b/res/layout/private_dns_mode_dialog.xml
index 4d614e2..0b4f4db 100644
--- a/res/layout/private_dns_mode_dialog.xml
+++ b/res/layout/private_dns_mode_dialog.xml
@@ -28,31 +28,20 @@
<RadioGroup
android:id="@+id/private_dns_radio_group"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/developer_option_dialog_margin_start">
- <RadioButton
+ <include
android:id="@+id/private_dns_mode_off"
- android:text="@string/private_dns_mode_off"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:minHeight="48dp"/>
+ layout="@layout/preference_widget_dialog_radiobutton"/>
- <RadioButton
+ <include
android:id="@+id/private_dns_mode_opportunistic"
- android:text="@string/private_dns_mode_opportunistic"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:minHeight="48dp"/>
+ layout="@layout/preference_widget_dialog_radiobutton"/>
- <RadioButton
+ <include
android:id="@+id/private_dns_mode_provider"
- android:text="@string/private_dns_mode_provider"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dp"
- android:minHeight="48dp"/>
+ layout="@layout/preference_widget_dialog_radiobutton"/>
<EditText
android:id="@+id/private_dns_mode_provider_hostname"
@@ -64,17 +53,12 @@
android:layout_height="wrap_content"
android:layout_marginStart="40dp"
android:layout_marginEnd="8dp"
- android:minHeight="48dp"/>
+ android:minHeight="@dimen/developer_option_dialog_min_height"/>
</RadioGroup>
- <TextView
+ <include
android:id="@+id/private_dns_help_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:minHeight="48dp"
- android:paddingStart="16dp"
- android:textAppearance="?android:attr/textAppearanceSmall"/>
+ layout="@layout/preference_widget_dialog_summary"/>
</LinearLayout>
</ScrollView>
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index c8fd6ce..c995c3e 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -156,8 +156,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/wifi_item_spinner"
- android:prompt="@string/please_select_phase2"
- android:entries="@array/wifi_phase2_entries" />
+ android:prompt="@string/please_select_phase2" />
</LinearLayout>
<LinearLayout android:id="@+id/l_ca_cert"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 09704ee..8407d1c 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Maak die teks op die skerm kleiner of groter."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Maak kleiner"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Maak groter"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Voorbeeldteks"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Die Wonderlike Towenaar van Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Hoofstuk 11: Die Wonderlike Smaragstad van Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Onbeskibaar tydens vliegtuigmodus"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Dwangrekenaarmodus"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Verplig eksperimentele rekenaarmodus op sekondêre skerms"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Ignoreer gedwonge donkermodus"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ignoreer die gedwonge donkermoduskenmerk om altyd aan te wees"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privaatheid"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index dff62d6..8cb1cac 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"በማያ ገጽ ላይ ያለውን ጽሑፍ ያሳንሱ ወይም ያተልቁ።"</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"አነስ አድርግ"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"ተለቅ አድርግ"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"የናሙና ጽሑፍ"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"አስደናቂው የኦዝ ምትሃተኛ"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"ምዕራፍ 11፦ ኦዝ፣ አስደናቂዋ የኤምራልድ ከተማ"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"በአይሮፕላን ሁነታ ወቅት የማይገኝ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"የዴስክቶፕ ሁነታን አስገድድ"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"በሁለተኛ ማሳያዎች ላይ የሙከራ ዴስክቶፕ ሁነታን አስገድድ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"አስገዳጅ ጨለማን ሻር"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"አስገዳጅ ጨለማ ባህሪን ሁልጊዜ እንዲበራ የተቀመጠውን ደንብ ይሽራል"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ግላዊነት"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index aed540a..92ac06f 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -4855,6 +4855,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"غير متاح أثناء وضع الطائرة"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"فرض وضع سطح المكتب"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"فرض وضع سطح المكتب التجريبي على شاشات العرض الثانوية"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"إلغاء ميزة فرض تعتيم الشاشة"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"لإلغاء ميزة فرض تعتيم الشاشة بحيث يتم تفعيلها دائمًا"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"الخصوصية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 9bc505e..be912be 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"এয়াৰপ্লেইন ম’ড অন থাকিলে উপলব্ধ নহয়"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"বলপূৰ্বক ডেস্কটপ ম’ড"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"দ্বিতীয় ডিছপ্লে’ত পৰীক্ষামূলক ডেস্কটপ ম’ড বলপূৰ্বকভাৱে প্ৰয়োগ কৰক"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"বলপূর্বকভাৱে গাঢ় ৰঙৰ থীম ব্যৱহাৰ কৰা সুবিধাটো অ\'ভাৰৰাইড কৰক"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"বলপূর্বকভাৱে গাঢ় ৰঙৰ থীম ব্যৱহাৰ কৰা সুবিধাটো সদায় অন হৈ থাকিবলৈ অ\'ভাৰৰাইড কৰে"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"গোপনীয়তা"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 59e1a07..126b2ba 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Təyyarə rejimində əlçatan deyil"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Masaüstü rejiminə keçin"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"İkinci displeylərdə eksperimental masaüstü rejimə keçin"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Qaranlıq rejimini ləğv edir"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Qaranlıq rejiminin həmişə aktiv olmasını ləğv edir"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Məxfilik"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 1bca73a..7ccddab 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -52,8 +52,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Omogućava da tekst na ekranu bude manji ili veći."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Umanji"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Uvećaj"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Primer teksta"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Čarobnjak iz Oza"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Poglavlje 11: Čudesni Smaragdni grad Oza"</string>
@@ -4605,6 +4604,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nedostupno tokom režima rada u avionu"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Nametni režim računara"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Nametni eksperimentalni režim računara na sekundarnim ekranima"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Zameni funkciju nametanja tamne teme"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Menja funkciju nametanja tamne teme tako da bude uvek uključena"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatnost"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 96c5924..449b0df 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -4691,6 +4691,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Недаступна ў рэжыме палёту"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Прымусова ўключаць рэжым працоўнага стала"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Прымусова ўключаць эксперыментальны рэжым працоўнага стала на дадатковых экранах"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Перавызначэнне ўключэння цёмнай тэмы"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Змяняе наладу прымусовага ўключэння цёмнай тэмы на \"Заўсёды ўключана\""</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Прыватнасць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index e7d5d9a..3497e3b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Намаляване или уголемяване на текста на екрана."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Намаляване на размера"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Увеличаване на размера"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Примерен текст"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Вълшебникът от Оз"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Глава 11: Удивителният изумруден град на Оз"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Не е налице в самолетен режим"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Принудително задаване на настолен режим"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Принудително задаване на експерименталния настолен режим на алтернативни дисплеи"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Отмяна на налагането на тъмен режим"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Отменя постоянното включено състояние на функцията за налагане на тъмен режим"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Поверителност"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 0e41c1a..045c03e 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"বিমান মোডে উপলভ্য নয়"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ফোর্স ডেস্কটপ মোড"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"সেকেন্ডারি ডিসপ্লেতে ফোর্স এক্সপেরিমেন্টাল ডেস্কটপ মোড চালু করা আছে"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ফোর্স-ডার্ক ফিচার ওভাররাইড করুন"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ফোর্স-ডার্ক ফিচার চালু রাখার জন্য ওভাররাইড করা হয়"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"গোপনীয়তা"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index ecf7a55..c10f39a 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -52,8 +52,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Smanjite ili povećajte tekst na ekranu."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Napravi manji"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Napravi veći"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Uzorak teksta"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Čarobnjak iz Oza"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Poglavlje 11: Smaragdni grad Oz"</string>
@@ -4605,6 +4604,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nije dostupno dok je aktiviran način rada u avionu"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Nametni način rada na računaru"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Nametanje eksperimentalnog načina rada na računaru na sekundarnim ekranima"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Nadjačaj nametanje tamne teme"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Nadjačava funkciju nametanja tamne teme tako da je uvijek uključena"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatnost"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 70fdb0f..4b62add 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"No disponible en mode d\'avió"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Força el mode d\'escriptori"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Força el mode d\'escriptori experimental en pantalles secundàries"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Substitueix forçar el mode fosc"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Substitueix la funció forçar el mode fosc perquè estigui sempre activada"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privadesa"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 24ee782..8f28ae8 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -53,8 +53,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Umožňuje zvětšit nebo zmenšit text na obrazovce."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Zmenšit"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Zvětšit"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Příliš žluťoučký kůň úpěl ďábelské ódy."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Ukázkový text"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Čaroděj ze země Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Kapitola 11: Smaragdové město v zemi Oz"</string>
@@ -4688,6 +4687,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"V režimu Letadlo nedostupné"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Vynutit režim počítače"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Vynutit na sekundárních displejích experimentální režim počítače"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Přepsat vynucení tmavého režimu"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Přepíše vynucení stálého zapnutí tmavého režimu"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Ochrana soukromí"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 4a29a76..014e5ef 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Gør teksten på skærmen mindre eller større."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Formindsk"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Forstør"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Eksempeltekst"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Den vidunderlige troldmand fra Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Kapitel 11: Den vidunderlige smaragdby i Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Der er ingen forbindelse i flytilstand"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Gennemtving skrivebordstilstand"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Gennemtving eksperimentel skrivebordstilstand på sekundære skærme"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Tilsidesæt force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Tilsidesætter funktionen force-dark og anvender Altid aktiveret"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatliv"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index f897477..fb0fad9 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Im Flugmodus nicht verfügbar"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Desktop-Modus erzwingen"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Experiementellen Desktop-Modus auf sekundären Bildschirmen erzwingen"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Erzwingen des dunklen Modus außer Kraft setzen"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Apps werden nicht gezwungen, den dunklen Modus zu verwenden"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Datenschutz"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 54c7d9a..3988bb1 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Κάντε το κείμενο στην οθόνη μικρότερο ή μεγαλύτερο."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Να γίνουν μικρότερα"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Να γίνουν μεγαλύτερα"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Δείγμα κειμένου"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Ο Θαυμάσιος Μάγος του Οζ"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Κεφάλαιο 11: Η Θαυμάσια Πόλη Έμεραλντ του Οζ"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Μη διαθέσιμο κατά τη λειτουργία πτήσης"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Αναγκαστική λειτουργία επιτραπέζιου υπολογιστή"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Αναγκαστική πειραματική λειτουργία επιτραπέζιου υπολογιστή σε δευτερεύουσες οθόνες"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Παράκαμψη αναγκ. χρήσης σκούρου θέματος"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Παρακάμπτει τη λειτουργία σκούρου θέματος προκειμένου να είναι πάντα ενεργή"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Απόρρητο"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 53ae032..5afc364 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Make the text on screen smaller or larger."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Make smaller"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Make larger"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Sample text"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Chapter 11: The Wonderful Emerald City of Oz"</string>
@@ -4522,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Unavailable during airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode on secondary displays"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Enable free-form sizecompat"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Allows sizecompat apps to be in free form"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Override force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overrides the force-dark feature to be always on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 333e928..331a256 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Make the text on screen smaller or larger."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Make smaller"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Make larger"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Sample text"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Chapter 11: The Wonderful Emerald City of Oz"</string>
@@ -4522,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Unavailable during airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode on secondary displays"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Enable free-form sizecompat"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Allows sizecompat apps to be in free form"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Override force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overrides the force-dark feature to be always on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 53ae032..5afc364 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Make the text on screen smaller or larger."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Make smaller"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Make larger"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Sample text"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Chapter 11: The Wonderful Emerald City of Oz"</string>
@@ -4522,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Unavailable during airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode on secondary displays"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Enable free-form sizecompat"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Allows sizecompat apps to be in free form"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Override force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overrides the force-dark feature to be always on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index afdfd62..47df8c6 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Make the text on screen smaller or larger."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Make smaller"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Make larger"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Sample text"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Chapter 11: The Wonderful Emerald City of Oz"</string>
@@ -4522,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Unavailable during airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode on secondary displays"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Enable free-form sizecompat"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Allows sizecompat apps to be in free form"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Override force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overrides the force-dark feature to be always on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 3ea5d85..19683ed 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -4521,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Unavailable during airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode on secondary displays"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Enable freeform sizecompat"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Allows sizecompat apps to be in freeform"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Override force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overrides the force-dark feature to be always-on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 6f4983d..62343e3 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Aumenta o reduce el tamaño del texto en pantalla."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Reducir el tamaño"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Aumentar el tamaño"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Texto de muestra"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"El maravilloso mago de Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capítulo 11: La maravillosa Ciudad Esmeralda de Oz"</string>
@@ -4520,6 +4519,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"No disponible en modo de avión"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forzar modo de escritorio"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forzar el modo de escritorio experimental en pantallas secundarias"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Anular forzar el modo oscuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Anula la función forzar el modo oscuro para que esté siempre activo"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidad"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 3bdda5d..5fc8723 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Aumenta o disminuye el tamaño del texto de la pantalla."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Reducir el tamaño"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Aumentar el tamaño"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Texto de ejemplo"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"El maravilloso mago de Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capítulo 11: La maravillosa Ciudad Esmeralda de Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"No están disponibles en modo avión"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forzar modo de escritorio"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forzar el modo de escritorio experimental en pantallas secundarias"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Forzar el modo oscuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Forzar el modo oscuro para que esté siempre activo"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidad"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index c1dfec3..5eed16d 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Muutke ekraanil kuvatavat tekst suuremaks või väiksemaks."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Vähendamine"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Suurendamine"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Näidistekst"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Võlur Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. peatükk: Ozi imeline smaragdlinn"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ei ole lennukirežiimis saadaval"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Töölauarežiimi jõustamine"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Katselise töölauarežiimi jõustamine teisestel ekraanidel"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Funktsiooni Sunnitud tume alistamine"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Alistab funktsiooni Sunnitud tume ja lülitab selle alaliselt sisse"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privaatsus"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index c6420e2..06e33c7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Handitu edo txikitu pantailako testua."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Txikitu"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Handitu"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Testu-lagina"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Ozeko azti miragarria"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. kapitulua: Esmeraldazko Oz hiri harrigarria"</string>
@@ -3397,8 +3396,7 @@
<string name="notification_assistant_title" msgid="6983941403582134437">"Jakinarazpen doigarriak"</string>
<string name="no_notification_assistant" msgid="2533323397091834096">"Bat ere ez"</string>
<string name="no_notification_listeners" msgid="2839354157349636000">"Ez dago jakinarazpenetarako sarbidea eskatu duen aplikaziorik."</string>
- <!-- no translation found for notification_access_detail_switch (46386786409608330) -->
- <skip />
+ <string name="notification_access_detail_switch" msgid="46386786409608330">"Eman jakinarazpenak atzitzeko baimena"</string>
<string name="notification_assistant_security_warning_title" msgid="2972346436050925276">"<xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari jakinarazpenetarako sarbidea eman nahi diozu?"</string>
<string name="notification_assistant_security_warning_summary" msgid="3119843404577399036">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> aplikazioak jakinarazpen guztiak irakurri ahalko ditu eta, haien barnean, informazio pertsonala egon daiteke (adibidez, kontaktuen izenak eta jasotzen dituzun testu-mezuak). Gainera, jakinarazpen horiek aldatu edo baztertu egin ahal izango ditu, baita haietako ekintza-botoiak sakatu ere. \n\nHorrez gain, ez molestatzeko modua aktibatzeko eta desaktibatzeko aukera izango du aplikazioak, baita harekin erlazionatutako ezarpenak aldatzekoa ere."</string>
<string name="notification_listener_security_warning_title" msgid="5791700876622858363">"Jakinarazpenak atzitzeko baimena eman nahi diozu <xliff:g id="SERVICE">%1$s</xliff:g> zerbitzuari?"</string>
@@ -4523,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ez dago erabilgarri hegaldi moduan"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Behartu mahaigainerako modua erabiltzera"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Behartu mahaigainerako modu esperimentala erabiltzera bigarren mailako pantailetan"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Ez aplikatu gai iluna"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Gai iluna beti aktibatuta egoteko eskakizuna gainidazten du"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Pribatutasuna"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index d5600bd..3ca42f6 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"نوشتار روی صفحهنمایش را بزرگتر یا کوچکتر کنید."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"کوچکتر کردن"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"بزرگتر کردن"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"برای این آقا آبجو و کیوی سرو کنید."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"نوشتار نمونه"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"جادوگر شهر اوز"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"فصل ۱۱: شهر زمردی شگفتانگیز اوز"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"عدمدسترسی در حالت هواپیما"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"اجرای اجباری حالت دسکتاپ"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"اجرای اجباری حالت دسکتاپ آزمایشی در نمایشگرهای ثانوی"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"لغو اجبار حالت تاریک"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ویژگی اجبار حالت تاریک را برای همیشه روشن بودن لغو میکند"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"حریم خصوصی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index e1507a0..6a29a74 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Suurenna tai pienennä tekstiä näytöllä."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Pienennä"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Suurenna"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Esimerkkiteksti"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Ihmemaa Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Luku 11: Ozin ihastuttava smaragdikaupunki"</string>
@@ -3397,8 +3396,7 @@
<string name="notification_assistant_title" msgid="6983941403582134437">"Mukautuvat ilmoitukset"</string>
<string name="no_notification_assistant" msgid="2533323397091834096">"–"</string>
<string name="no_notification_listeners" msgid="2839354157349636000">"Yksikään asennettu sovellus ei ole pyytänyt ilmoituksien käyttöoikeutta."</string>
- <!-- no translation found for notification_access_detail_switch (46386786409608330) -->
- <skip />
+ <string name="notification_access_detail_switch" msgid="46386786409608330">"Salli pääsy ilmoituksiin"</string>
<string name="notification_assistant_security_warning_title" msgid="2972346436050925276">"Saako <xliff:g id="SERVICE">%1$s</xliff:g> ilmoitusten käyttöoikeuden?"</string>
<string name="notification_assistant_security_warning_summary" msgid="3119843404577399036">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> voi lukea kaikki ilmoitukset ja niiden sisältämät henkilökohtaiset tiedot, kuten yhteystietojen nimet ja saamiesi viestien sisällön. Se voi myös ohittaa ilmoitukset, muokata niitä tai käyttää niiden toimintopainikkeita. \n\nTämä antaa sovellukselle myös oikeuden ottaa Älä häiritse ‑tilan käyttöön, poistaa sen käytöstä ja muokata siihen liittyviä asetuksia."</string>
<string name="notification_listener_security_warning_title" msgid="5791700876622858363">"Saako <xliff:g id="SERVICE">%1$s</xliff:g> ilmoituksien käyttöoikeuden?"</string>
@@ -4523,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ei käytettävissä lentokonetilassa"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Pakota työpöytätila"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Pakota kokeellinen työpöytätila toissijaisille näytöille."</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Ohita SmartDarkin pakottaminen päälle"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ohittaa toiminnon, joka pakottaa SmartDark-ominaisuuden aina päälle"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Tietosuoja"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 8727516..2eaee73 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Données cellulaires non accessibles en mode Vol"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forcer le mode bureau"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forcer le mode bureau expérimental sur les écrans secondaires"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Forcer l\'activation de SmartDark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Forcer la fonctionnalité SmartDark à rester active"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Confidentialité"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8a3feaa..ddb64d5 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Agrandissez ou réduisez la taille du texte affiché."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Réduire"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Agrandir"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Exemple de texte"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Le Magicien d\'Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Chapitre 11 : La merveilleuse cité d\'émeraude"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Non disponibles en mode Avion"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forcer l\'activation du mode bureau"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forcer l\'activation du mode bureau expérimental sur les écrans secondaires"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Forcer l\'activation de Smart Dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Force la fonctionnalité Smart Dark à être toujours activée"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Confidentialité"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 93b2203..c12fbe3 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Non dispoñibles no modo avión"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forzar modo de escritorio"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forza o modo de escritorio experimental en pantallas secundarias"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Anular Forzar modo escuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Anula a función Forzar modo escuro para que estea sempre activo"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidade"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 8810653..2ebf342 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"એરપ્લેન મોડ દરમિયાન ઉપલબ્ધ નથી"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ફરજિયાત ડેસ્કટૉપ મોડ ચાલુ કરો"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ગૌણ ડિસ્પ્લે પર ફરજિયાત પ્રયોગાત્મક ડેસ્કટૉપ મોડ ચાલુ કરો"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ફરજિયાત ઘેરા મોડની સુવિધાને ઓવરરાઇડ કરો"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"હંમેશાં ચાલુ રાખવા માટે ફરજિયાત ઘેરા મોડની સુવિધાને ઓવરરાઇડ કરે છે"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"પ્રાઇવસી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b950b90..b128309 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"हवाई जहाज़ मोड के दौरान उपलब्ध नहीं"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"डेस्कटॉप मोड चालू करें"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"दूसरी स्क्रीन पर प्रयोग के लिए डेस्कटॉप मोड चालू करें"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"फ़ोर्स-डार्क सुविधा को ओवरराइड करें"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"फ़ोर्स-डार्क सुविधा को हमेशा चालू रहने के लिए ओवरराइड करती है"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"निजता"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 7261b6f..5a8c1fc 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -52,8 +52,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Smanjite ili povećajte tekst na zaslonu."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Smanji"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Povećaj"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Primjer teksta"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Čarobnjak iz Oza"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. poglavlje: Čudesni Smaragdni Grad čarobnjaka Oza"</string>
@@ -4605,6 +4604,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nije dostupno u načinu rada u zrakoplovu"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Nametni način rada na računalu"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Nametanje eksperimentalnog načina rada na računalu na sekundarnim zaslonima"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Nadjačaj nametanje tamne teme"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Nadjačava značajku nametanja tamne teme tako da je uvijek uključena"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatnost"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index c0b6ddd..a3c60c4 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Kicsinyítheti vagy nagyíthatja a képernyőn megjelenő szöveget."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Kisebb"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Nagyobb"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Árvíztűrő tükörfúrógép"</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Mintaszöveg"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Óz, a csodák csodája"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. fejezet: A csodálatos Smaragdváros"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Repülős üzemmódban nem áll rendelkezésre"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Asztali mód kényszerítése"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Kísérleti asztali mód kényszerítése a másodlagos kijelzőkön"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"A kényszerített sötétítés felülírása"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"A kényszerített sötétítés funkció felülírása mindig bekapcsolt állapotra"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Adatvédelem"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 380cf04..0f49c13 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Փոփոխեք տեքստի չափը։"</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Փոքրացնել"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Մեծացնել"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Տեքստի նմուշ"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Օզի կախարդը"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Գլուխ 11. Օզի հրաշալի Զմրուխտե քաղաքը"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Անհասանելի է ավիառեժիմում"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Աշխատասեղանի ռեժիմի հարկադրված միացում"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Աշխատասեղանի փորձնական ռեժիմի հարկադրված միացում լրացուցիչ էկրանների վրա"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Չեղարկել մուգ ռեժիմի պարտադիր միացումը"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Չեղարկում է մուգ ռեժիմի պարտադիր միացման գործառույթը"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Գաղտնիություն"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index b9d7bb2..c26af80 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Buat teks di layar jadi lebih kecil atau lebih besar."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Buat lebih kecil"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Buat lebih besar"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Teks contoh"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Penyihir Oz yang Menakjubkan"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Bab 11: Oz, Kota Zamrud yang Menakjubkan"</string>
@@ -4522,6 +4521,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Tidak tersedia selama mode pesawat"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Paksa mode desktop"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Paksa mode desktop eksperimental di tampilan sekunder"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Aktifkan sizecompat format bebas"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Mengizinkan aplikasi sizecompat dalam format bebas"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Aktifkan paksa SmartDark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Memaksa fitur SmartDark agar selalu aktif"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privasi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 0ff3fd5..ba79b9f 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Stækkaðu eða minnkaðu texta á skjá."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Minnka"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Stækka"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Textadæmi"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Galdrakarlinn í Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. kafli: Smaragðsborgin dásamlega í Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ekki tiltækt í flugstillingu"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Þvinga skjáborðsstillingu"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Þvinga tilraunaskjáborðsstillingu á öðrum skjá"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Hnekkja dökku"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Kemur í veg fyrir að alltaf sé kveikt á dökkum eiginleika"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Persónuvernd"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 8f866ea..d7bf5b7 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Ingrandisci o riduci il testo sullo schermo."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Rimpicciolisci"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Ingrandisci"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Testo di esempio"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Il meraviglioso mago di Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capitolo 11: La splendida Città di smeraldo di Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Non disponibili in modalità aereo"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forza la modalità desktop"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forza la modalità desktop sperimentale su display secondari"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Esegui override modalità Buio forzata"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Esegue l\'override della funzionalità di modalità Buio forzata in modo che sia sempre attiva"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 429d4af..a31cc4e 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -4691,6 +4691,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"לא זמין במצב טיסה"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"אילוץ מצב שולחן עבודה"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"אילוץ מצב שולחן עבודה ניסיוני במסכים משניים"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"שינוי התכונה SmartDark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"התכונה SmartDark תפעל כל הזמן"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"פרטיות"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index e7acffb..947a6e5 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"機内モードの間は使用できません"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"デスクトップ モードに強制的に切り替え"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"セカンダリ ディスプレイで試験運用版デスクトップ モードに強制的に切り替えます"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"フォースダークのオーバーライド"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"フォースダーク機能をオーバーライドして常に ON にする"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"プライバシー"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 5febb94..2468aee 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"თვითმფრინავის რეჟიმში მიუწვდომელია"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"დესკტოპის რეჟიმის იძულებით გააქტიურება"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"დესკტოპის ექსპერიმენტული რეჟიმის იძულებით გააქტიურება მეორეულ ეკრანებზე"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"გამუქების იძულების უგულებელყოფა"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"უგულებელყოფს გამუქების ყოველთვის იძულების ფუნქციას"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"კონფიდენციალურობა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index eae4aca..a754715 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ұшақ режимінде қолжетімді емес."</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Жұмыс үстелінің режимін мәжбүрлі қосу"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Қосымша дисплейлерде эксперименттік жұмыс үстелі режимін мәжбүрлі қосу"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Қараңғылауға жол бермеу"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Қараңғы тақырыпты мәжбүрлеп қойғанда, \"Әрдайым қосулы\" мәніне өзгертеді"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Құпиялылық"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index e7561d0..3ba1ac4 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"មិនអាចប្រើបានទេ ពេលប្រើមុខងារពេលជិះយន្តហោះ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"បង្ខំមុខងារអេក្រង់ដើម"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"បង្ខំមុខងារអេក្រង់ដើមសាកល្បងនៅលើអេក្រង់ទីពីរ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"លុបពីលើមុខងារបង្ខំឱ្យងងឹត"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"លុបពីលើមុខងារបង្ខំឱ្យងងឹតឱ្យបើកជានិច្ច"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ឯកជនភាព"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 6744ee2..b941add 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ಏರ್ಪ್ಲೇನ್ ಮೋಡ್ನಲ್ಲಿರುವಾಗ ಲಭ್ಯವಿರುವುದಿಲ್ಲ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ಡೆಸ್ಕ್ಟಾಪ್ ಮೋಡ್ ಅನ್ನು ಒತ್ತಾಯ ಮಾಡಿ"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ಸೆಕೆಂಡರಿ ಡಿಸ್ಪ್ಲೇಗಳಲ್ಲಿ ಪ್ರಾಯೋಗಿಕ ಡೆಸ್ಕ್ಟಾಪ್ ಮೋಡ್ ಅನ್ನು ಒತ್ತಾಯ ಮಾಡಿ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ಫೋರ್ಸ್-ಡಾರ್ಕ್ ವೈಶಿಷ್ಟ್ಯ ಬಲಗೊಳಿಸಲು ಸೆಟ್ಟಿಂಗ್ ಅತಿಕ್ರಮಿಸಿ"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ಫೋರ್ಸ್-ಡಾರ್ಕ್ ವೈಶಿಷ್ಟ್ಯವು ಆನ್ ಆಗಿರುವಂತೆ ಒತ್ತಾಯಿಸುತ್ತದೆ"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ಗೌಪ್ಯತೆ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 9d3d37f..1876905 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"비행기 모드에서는 사용할 수 없음"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"데스크톱 모드 강제 사용"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"보조 디스플레이에서 실험적 데스크톱 모드 강제 사용"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"강제로 어둡게 재정의"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"강제로 어둡게 기능을 항상 사용하도록 재정의"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"개인정보 보호"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index c6e2038..f49f2f2 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Учак режиминде жеткиликсиз"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Иш такта режимин күйгүзүү"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Тажрыйбалык иш такта режимин көмөкчү экрандарда иштетүү"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Түнкү режимди мажбурлап иштетүү функциясын өзгөртүп коюу"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Түнкү режимди мажбурлап иштетүү функциясы ар дайым күйүк болгондой кылып өзгөртүп коёт"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Купуялык"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 9c6c832..4c3c4e1 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ບໍ່ສາມາດໃຊ້ໄດ້ໃນໂໝດຢູ່ໃນຍົນ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ບັງຄັບໂໝດເດັສທັອບ"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ບັງຄັບໂໝດເດັສທັອບແບບທົດລອງໃຊ້ຢູ່ໜ້າຈໍທີສອງ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ຍົກເລີກການບັງຄັບໃຫ້ໃຊ້ແບບມືດ"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ຍົກເລີກຄຸນສົມບັດການບັງຄັບໃຫ້ໃຊ້ແບບມືດຕະຫຼອດ"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ຄວາມເປັນສ່ວນຕົວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d8db1ae..fdd6131 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -4688,6 +4688,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nepasiekiama lėktuvo režimu"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Priverstinai įjungti darbalaukio režimą"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Priverstinai įjungti eksperimentinį darbalaukio režimą antriniuose ekranuose"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Nepaisyti priverst. tamsaus rež. įgalin."</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Nepaisoma priverstinio tamsaus režimo funkcijos įgalinimo, kad ji visada būtų įjungta"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatumas"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index f7eae5e..01b857a 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -4606,6 +4606,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nav pieejami lidojuma režīmā"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Veikt piespiedu darbvirsmas režīma atvēršanu"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Veikt piespiedu eksperimentālā darbvirsmas režīma atvēršanu sekundārajos displejos"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Tumšā režīma piespiedu ieslēgšanas ignorēšana"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ignorē tumšā režīma iestatījumu Vienmēr ieslēgts"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Konfidencialitāte"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index fd97993..62ecfbe 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Намалете го или зголемете го текстот на екранот."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Намали"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Зголеми"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Примерок на текст"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Чудесниот волшебник од Оз"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Поглавје 11: Чудесниот смарагден град Оз"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Недостапен во авионски режим"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Наметни режим на работна површина"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Наметнете го експерименталниот режим на работна површина на секундарните екрани"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Отфрли го наметнувањето темен режим"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ја отфрла функцијата за наметнување темен режим за да биде постојано вклучена"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Приватност"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index aac60c2..084178f 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ഫ്ലൈറ്റ് മോഡിൽ ലഭ്യമല്ല"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ഡെസ്ക്ടോപ്പ് മോഡിലേക്ക് നിർബന്ധിച്ച് വരുത്തുക"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ദ്വിതീയ ഡിസ്പ്ലേകളിൽ, പരീക്ഷണാത്മക ഡെസ്ക്ടോപ്പ് മോഡിലേക്ക് നിർബന്ധിച്ച് വരുത്തുക"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"നിർബന്ധിത ഇരുണ്ട മോഡ് അസാധുവാക്കുക"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"എപ്പോഴും ഓണാക്കിയിരിക്കുന്ന നിർബന്ധിത ഇരുണ്ട മോഡ് ഫീച്ചർ അസാധുവാക്കുക"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"സ്വകാര്യത"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 22554ca..9b8f4ee 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Дэлгэц дээрх текстийг томруулах, жижигрүүлэх."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Жижигрүүлэх"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Томруулах"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Жишээ текст"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Озын Гайхамшигт шидтэн"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Бүлэг 11: Озын Гайхамшигт Маргад эрдэнийн хот"</string>
@@ -4520,6 +4519,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Нислэгийн горимд боломжгүй"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Дэлгэцийн горимыг хүчлэх"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Дэлгэцийн туршилтын горимыг хоёрдогч дэлгэцэд хүчлэх"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Хүчээр бүдгэрүүлэхийг дарах"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Хүчээр бүдэгрүүлэх онцлогийг байнга асаалттай байхаар дардаг"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Нууцлал"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index b62f799..66c85ef 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"विमान मोड सुरू असताना उपलब्ध नाही"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"डेस्कटॉप मोडची सक्ती करा"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"दुसऱ्या डिस्प्लेवर प्रायोगिक डेस्कटॉप मोडला सक्ती करा"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"फोर्स डार्क ओव्हरराइड करते"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"फोर्स डार्क वैशिष्ट्य नेहमी सुरू ठेवण्यास ओव्हरराइड करते"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"गोपनीयता"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 52c5a42..aca3cff 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Tidak tersedia semasa dalam mod pesawat"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Paksa mod desktop"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Paksa mod desktop percubaan pada paparan kedua"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Batalkan paksa gelap"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Membatalkan ciri paksa gelap supaya sentiasa hidup"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privasi"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 6744af6..1681486 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"လေယာဉ်ပျံမုဒ် ပြုလုပ်ထားစဉ် သုံး၍မရပါ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ဒက်စ်တော့ပုံစံ မဖြစ်မနေ ပြောင်းခြင်း"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ဒုတိယ မျက်နှာပြင်ပြသမှုများတွင် ဒက်စ်တော့ပုံစံ မဖြစ်မနေ စမ်းသပ်ရန်"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"မဖြစ်မနေအမှောင်ချခြင်းကို အစားထိုးခြင်း"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"မဖြစ်မနေအမှောင်ချခြင်း ဝန်ဆောင်မှု အမြဲဖွင့်ရန် အစားထိုးထားသည်"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ကန့်သတ်ဆက်တင်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 8e91992..12adea2 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Utilgjengelig i flymodus"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Tvungen skrivebordsmodus"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Tvungen eksperimentell skrivebordsmodus på sekundære skjermer"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Overstyr tving mørk"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overstyrer tving mørk-funksjonen til å være alltid på"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Personvern"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index e7c8cab..5dec65f 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"हवाइजहाज मोडमा हुँदा मोबाइल डेटा उपलब्ध हुँदैन"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"डेस्कटप मोडमा बल गर्नुहोस्"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"सहायक प्रदर्शनहरूमा प्रयोगात्मक डेस्कटप मोडमा जोड दिनुहोस्"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"force-dark ओभरराइड गर्नुहोस्"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"force-dark सुविधा सधैँ सक्रिय हुन बल गर्छ"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"गोपनीयता"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 942d66c..7f2240f 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Maak de tekst op het scherm kleiner of groter."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Verkleinen"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Vergroten"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Voorbeeldtekst"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"De tovenaar van Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Hoofdstuk 11: De prachtige Smaragden Stad van Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Niet beschikbaar in de vliegtuigmodus"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Desktopmodus afdwingen"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Experimentele desktopmodus afdwingen op secundaire displays"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Gedwongen donker maken negeren"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Overschrijft de functie voor gedwongen donker maken zodat deze altijd aan is"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 8bf5559..01a5376 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ଏୟାରପ୍ଲେନ୍ ମୋଡ୍ ସମୟରେ ଉପଲବ୍ଧ ନୁହେଁ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ଫୋର୍ସ ଡେସ୍କଟପ୍ ମୋଡ୍"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ଦ୍ବିତୀୟ ପ୍ରଦର୍ଶନରେ ଫୋର୍ସ ପରୀକ୍ଷାମୂଳକ ମୋଡ୍"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ଫୋର୍ସ୍-ଡାର୍କ୍ ଓଭର୍ରାଇଡ୍ କରନ୍ତୁ"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ଫୋର୍ସ-ଡାର୍କ ଫିଚର୍ ସଦାବେଳେ ଚାଲୁ ରହିବା ଓଭର୍ରାଇଡ୍ କରିଥାଏ"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ଗୋପନୀୟତା"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index f373100..e3cbb41 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"ਸਕ੍ਰੀਨ \'ਤੇ ਦਿਖਦੀ ਲਿਖਤ ਨੂੰ ਛੋਟਾ ਜਾਂ ਵੱਡਾ ਕਰੋ।"</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"ਛੋਟਾ ਕਰੋ"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"ਵੱਡਾ ਕਰੋ"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"ਨਮੂਨਾ ਲਿਖਤ"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"ਔਜ਼ ਦਾ ਨਿਰਾਲਾ ਵਿਜ਼ਾਰਡ"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"ਪਾਠ 11: ਔਜ਼ ਦਾ ਹੀਰਿਆਂ-ਪੰਨਿਆਂ ਵਾਲਾ ਨਿਰਾਲਾ ਸ਼ਹਿਰ"</string>
@@ -3376,8 +3375,7 @@
<string name="notification_channel_summary_low" msgid="4842529455460294865">"ਤੁਹਾਨੂੰ ਬਿਨਾਂ ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ ਦੇ ਫੋਕਸ ਕਰਨ ਵਿੱਚ ਮਦਦ ਕਰਦਾ ਹੈ"</string>
<string name="notification_channel_summary_default" msgid="2919219975379032181">"ਧੁਨੀ ਅਤੇ ਥਰਥਰਾਹਟ ਨਾਲ ਤੁਹਾਡਾ ਧਿਆਨ ਖਿੱਚਦੀ ਹੈ"</string>
<string name="notification_channel_summary_high" msgid="3411637309360617621">"ਡੀਵਾਈਸ ਅਣਲਾਕ ਕਰਨ \'ਤੇ, ਸਕ੍ਰੀਨ ਦੇ ਸਿਖਰ \'ਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਬੈਨਰ ਵਜੋਂ ਦਿਖਾਓ"</string>
- <!-- no translation found for notification_switch_label (6022236996552304892) -->
- <skip />
+ <string name="notification_switch_label" msgid="6022236996552304892">"ਸਾਰੀਆਂ <xliff:g id="APP_NAME">%1$s</xliff:g> ਸੂਚਨਾਵਾਂ"</string>
<string name="default_notification_assistant" msgid="243718059890346442">"ਅਨੁਕੂਲ ਸੂਚਨਾਵਾਂ"</string>
<plurals name="notifications_sent_daily" formatted="false" msgid="1479283620504341566">
<item quantity="one">ਹਰ ਰੋਜ਼ ~<xliff:g id="NUMBER_1">%d</xliff:g> ਸੂਚਨਾ</item>
@@ -3398,8 +3396,7 @@
<string name="notification_assistant_title" msgid="6983941403582134437">"ਅਨੁਕੂਲ ਸੂਚਨਾਵਾਂ"</string>
<string name="no_notification_assistant" msgid="2533323397091834096">"ਕੋਈ ਨਹੀਂ"</string>
<string name="no_notification_listeners" msgid="2839354157349636000">"ਕਿਸੇ ਵੀ ਸਥਾਪਤ ਕੀਤੀਆਂ ਐਪਾਂ ਨੇ ਸੂਚਨਾ ਪਹੁੰਚ ਦੀ ਬੇਨਤੀ ਨਹੀਂ ਕੀਤੀ ਹੈ।"</string>
- <!-- no translation found for notification_access_detail_switch (46386786409608330) -->
- <skip />
+ <string name="notification_access_detail_switch" msgid="46386786409608330">"ਸੂਚਨਾ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦਿਓ"</string>
<string name="notification_assistant_security_warning_title" msgid="2972346436050925276">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਨੂੰ ਸੂਚਨਾ ਪਹੁੰਚ ਦੇਣੀ ਹੈ?"</string>
<string name="notification_assistant_security_warning_summary" msgid="3119843404577399036">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> ਸਾਰੀਆਂ ਸੂਚਨਾਵਾਂ ਪੜ੍ਹ ਸਕੇਗੀ ਜਿਸ ਵਿੱਚ ਸੰਪਰਕ ਨਾਮ ਅਤੇ ਤੁਹਾਨੂੰ ਪ੍ਰਾਪਤ ਹੋਣ ਵਾਲੇ ਲਿਖਤ ਸੁਨੇਹਿਆਂ ਵਰਗੀ ਨਿੱਜੀ ਜਾਣਕਾਰੀ ਸ਼ਾਮਲ ਹੁੰਦੀ ਹੈ। ਇਹ ਸੂਚਨਾਵਾਂ ਨੂੰ ਸੋਧਣ ਜਾਂ ਖਾਰਜ ਕਰਨ ਜਾਂ ਉਸ ਵਿੱਚ ਸ਼ਾਮਲ ਕਾਰਵਾਈ ਬਟਨਾਂ ਨੂੰ ਟ੍ਰਿਗਰ ਵੀ ਕਰ ਸਕੇਗੀ। \n\nਇਹ ਐਪ ਨੂੰ \'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਨੂੰ ਬੰਦ ਜਾਂ ਚਾਲੂ ਕਰਨ ਅਤੇ ਸੰਬੰਧਿਤ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲਣ ਦੀ ਸਮਰੱਥਾ ਵੀ ਦੇਵੇਗੀ।"</string>
<string name="notification_listener_security_warning_title" msgid="5791700876622858363">"ਕੀ <xliff:g id="SERVICE">%1$s</xliff:g> ਲਈ ਸੂਚਨਾ ਪਹੁੰਚ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string>
@@ -4524,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ਹਵਾਈ-ਜਹਾਜ਼ ਮੋਡ ਚਾਲੂ ਹੋਣ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ਜ਼ਬਰਦਸਤੀ ਡੈਸਕਟਾਪ ਮੋਡ ਵਿੱਚ ਲਿਆਓ"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ਸੈਕੰਡਰੀ ਡਿਸਪਲੇਆਂ \'ਤੇ ਜ਼ਬਰਦਸਤੀ ਪ੍ਰਯੋਗਮਈ ਡੈਸਕਟਾਪ ਮੋਡ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ਜ਼ਬਰਦਸਤੀ ਗੂੜ੍ਹਾ ਮੋਡ ਓਵਰਰਾਈਡ ਕਰੋ"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ਜ਼ਬਰਦਸਤੀ ਨਾਲ ਗੂੜ੍ਹਾ ਮੋਡ ਲਾਗੂ ਕਰਨ ਵਾਲੀ ਵਿਸ਼ੇਸ਼ਤਾ ਨੂੰ ਹਮੇਸ਼ਾਂ-ਚਾਲੂ ਰੱਖਣ ਲਈ ਓਵਰਰਾਈਡ ਕਰਦੀ ਹੈ"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ਪਰਦੇਦਾਰੀ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 54e72e2..c096adf3 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -4689,6 +4689,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Niedostępne w trybie samolotowym"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Wymuś tryb pulpitu"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Wymuś eksperymentalny tryb pulpitu na dodatkowych ekranach"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Zastąp wymuszanie trybu ciemnego"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Zastępuje ustawienie zawsze włączonej funkcji wymuszania trybu ciemnego"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Prywatność"</string>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index d445f84..581023b 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Tornar o texto na tela menor ou maior."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Tornar menor"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Tornar maior"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Texto de amostra"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"O Mágico de Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capítulo 11: A maravilhosa Cidade das Esmeraldas de Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Indisponível no modo avião"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forçar modo de área de trabalho"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forçar o modo de área de trabalho experimental em telas secundárias"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Modificar o recurso forçar modo escuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Modificar o recurso de forçar modo escuro para que fique sempre ativado"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidade"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index e134799..3e136ae 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Aumente ou diminua o texto no ecrã."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Diminuir"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Aumentar"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Exemplo de texto"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"O Maravilhoso Feiticeiro de Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capítulo 11: A Maravilhosa Cidade Esmeralda de Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Não disponível durante o modo de avião"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forçar modo de ambiente de trabalho"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forçar modo de ambiente de trabalho experimental em ecrãs secundários"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Substituir forçar o modo escuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Substitui a funcionalidade de forçar a ativação permanente do modo escuro"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidade"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d445f84..581023b 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Tornar o texto na tela menor ou maior."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Tornar menor"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Tornar maior"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Texto de amostra"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"O Mágico de Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Capítulo 11: A maravilhosa Cidade das Esmeraldas de Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Indisponível no modo avião"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forçar modo de área de trabalho"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forçar o modo de área de trabalho experimental em telas secundárias"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Modificar o recurso forçar modo escuro"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Modificar o recurso de forçar modo escuro para que fique sempre ativado"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacidade"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index e33896e..2162c15 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -4606,6 +4606,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nu sunt disponibile în modul Avion"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Forțați modul desktop"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Forțați modul desktop experimental pe ecrane secundare"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Modificați funcția Force-Dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Modifică funcția Force-Dark să fie activată mereu"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Confidențialitate"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 82c1da1..32d79c2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -4688,6 +4688,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Мобильный Интернет недоступен в режиме полета."</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Принудительное включение режима рабочего стола"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Принудительное включение экспериментального режима рабочего стола на дополнительных экранах"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Переопределение включения тёмной темы"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Изменение настройки принудительного включения тёмной темы на \"Всегда включено\""</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Конфиденциальность"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 5d81082..4e15d24 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"තිරය මත පෙළ වඩාත් කුඩා හෝ විශාල කරන්න."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"වඩා කුඩා කරන්න"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"වඩා විශාල කරන්න"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"ආදර්ශ පෙළ"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11වන පරිච්ඡේදය: The Wonderful Emerald City of Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ගුවන් යානා ප්රකාරයේදී නොලැබේ"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"වැඩතල ප්රකාරය බල කරන්න"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ද්විතීයික සංදර්ශන කෙරෙහි අත්හදා බැලීමේ වැඩතල ප්රකාරය බල කරන්න"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"force-dark ප්රතික්ෂේප කරන්න"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"force-dark විශේෂාංගය සැම විට ක්රියාත්මකව තිබීමට ප්රතික්ෂේප කරයි"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"රහස්යතාව"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index be992ca..130168a 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -4688,6 +4688,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nedostupné v režime v lietadle"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Vynútiť režim pracovnej plochy"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Vynútenie experimentálneho režimu pracovnej plochy na sekundárnych obrazovkách"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Presadiť tmavý režim"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Presadí, aby bol tmavý režim vždy zapnutý"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Ochrana súkromia"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 460ab3d..867ff6d 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -53,8 +53,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Pomanjšava ali povečava besedila na zaslonu"</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Pomanjšanje"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Povečanje"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Vzorčno besedilo"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Čudoviti čarovnik iz Oza"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11. poglavje: Čudovito Smaragdno mesto"</string>
@@ -4688,6 +4687,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ni na voljo v načinu za letalo"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Vsili namizni način"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Vsili poskusni namizni način na sekundarnih zaslonih"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Preglasi vsiljenje temnega načina"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Preglasi vsiljenje vedno vklopljenega temnega načina"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Zasebnost"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 283a918..d2239bb 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Bëje tekstin në ekran më të vogël ose më të madh."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Zvogëlo"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Zmadho"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Teksti shembull"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Magjistari i mrekullueshëm i Ozit"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Kapitulli 11: Qyteti i mrekullueshëm i smeraldtë i Ozit"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Nuk ofrohen në modalitetin e aeroplanit"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Detyro \"modalitetin e desktopit\""</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Detyro \"modalitetin e desktopit\" eksperimental në ekrane dytësore"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Anulo detyrimin e errësimit"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Anulon funksionin e detyrimit të errësimit që të jetë gjithmonë aktiv"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privatësia"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 2b42b2d..7ecb8fa 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -52,8 +52,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Омогућава да текст на екрану буде мањи или већи."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Умањи"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Увећај"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Пример текста"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Чаробњак из Оза"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Поглавље 11: Чудесни Смарагдни град Оза"</string>
@@ -4605,6 +4604,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Недоступно током режима рада у авиону"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Наметни режим рачунара"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Наметни експериментални режим рачунара на секундарним екранима"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Замени функцију наметања тамне теме"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Мења функцију наметања тамне теме тако да буде увек укључена"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Приватност"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index f6d73bd..68870f0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Gör texten på skärmen större eller mindre."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Förminska"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Förstora"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Exempeltext"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Den fantastiska trollkarlen från Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Kapitel 11: Den underbara Smaragdstaden i Oz"</string>
@@ -4522,6 +4521,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ej tillgängligt i flygplansläge"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Tvinga fram skrivbordsläge"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Tvinga fram experimentellt skrivbordsläge på sekundära skärmar"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Åsidosätter tvingat mörkt läge"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Åsidosätter funktionen som tvingar mörkt läge att alltid vara aktiverat"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Sekretess"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 7572ec9..42f145c 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Haipatikani ukitumia hali ya ndegeni"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Lazimisha hali ya eneo-kazi"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Lazimisha hali ya jaribio la eneo-kazi kwenye maonyesho yasiyo ya msingi"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Badilisha ulazimishaji wa mandhari meusi"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Hubatilisha mipangilio ya kipengele cha kulazimisha mandhari meusi ili kiwake kila wakati"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Faragha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index c255a66..eb3a91a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"விமானப் பயன்முறையின்போது கிடைக்காது"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"கட்டாய டெஸ்க்டாப் பயன்முறை"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"இரண்டாம்நிலை திரைகளில் \'கட்டாயப் பரிசோதனை டெஸ்க்டாப்\' பயன்முறை"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"force-dark அம்சத்தை மேலெழுதுதல்"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"எப்போதும் இயக்கத்தில் இருக்குமாறு force-dark அம்சத்தை மேலெழுதுகிறது"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"தனியுரிமை"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 5f763d6..6cc86be 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -4526,6 +4526,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"విమానం మోడ్లో సేవ అందుబాటులో ఉండదు"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ఫోర్స్ డెస్క్టాప్ మోడ్"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ద్వితీయ ప్రదర్శనల్లో ఫోర్స్ ప్రయోగాత్మక డెస్క్టాప్ మోడ్"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ఫోర్స్-డార్క్ను అధిగమించడం"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ఫోర్స్-డార్క్ ఫీచర్ను అధిగమించడం ఎల్లప్పుడూ ఆన్లో ఉండాలి"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"గోప్యత"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 7a5938e..1b63234 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ใช้ขณะที่เปิดโหมดบนเครื่องบินไม่ได้"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"บังคับใช้โหมดเดสก์ท็อป"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"บังคับใช้โหมดเดสก์ท็อปแบบทดลองในจอแสดงผลรอง"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"ลบล้างฟีเจอร์บังคับใช้โหมดมืด"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"ลบล้างฟีเจอร์บังคับใช้โหมดมืดให้เปิดตลอดเวลา"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"ความเป็นส่วนตัว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index c116359..61df992 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Hindi available sa airplane mode"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Force desktop mode"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Force experimental desktop mode sa mga pangalawang display"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"I-override ang force-dark"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ino-override ang force-dark feature para maging palaging naka-on"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Privacy"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index ffe1a88..fc02800 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Uçak modunda kullanılamaz"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Masaüstü modunu zorunlu kıl"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"İkincil ekranlarda deneysel masaüstü modunu zorunlu kıl"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Zorunlu koyu rengi geçersiz kıl"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Zorunlu koyu renk özelliğini geçersiz kılma her zaman açık"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Gizlilik"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 6e57214..cbe50c8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -53,8 +53,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Збільшуйте або зменшуйте текст на екрані."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Зменшити"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Збільшити"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Зразок тексту"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Чарівник країни Оз"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Глава 11. Дивовижне Смарагдове місто країни Оз"</string>
@@ -3495,8 +3494,7 @@
<string name="notification_assistant_title" msgid="6983941403582134437">"Адаптивні сповіщення"</string>
<string name="no_notification_assistant" msgid="2533323397091834096">"Немає"</string>
<string name="no_notification_listeners" msgid="2839354157349636000">"Установлені додатки не запитували доступу до сповіщень."</string>
- <!-- no translation found for notification_access_detail_switch (46386786409608330) -->
- <skip />
+ <string name="notification_access_detail_switch" msgid="46386786409608330">"Надати доступ до сповіщень"</string>
<string name="notification_assistant_security_warning_title" msgid="2972346436050925276">"Надати додатку <xliff:g id="SERVICE">%1$s</xliff:g> доступ до сповіщень?"</string>
<string name="notification_assistant_security_warning_summary" msgid="3119843404577399036">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> зможе переглядати всі сповіщення, зокрема з особистою інформацією, як-от іменами контактів і текстами отриманих повідомлень. Він також зможе змінювати й відхиляти сповіщення та активувати в них командні кнопки. \n\nДодаток зможе вмикати й вимикати режим \"Не турбувати\" та змінювати пов’язані з ним налаштування."</string>
<string name="notification_listener_security_warning_title" msgid="5791700876622858363">"Надати додатку <xliff:g id="SERVICE">%1$s</xliff:g> доступ для сповіщень?"</string>
@@ -4689,6 +4687,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Недоступно, коли ввімкнено режим польоту"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Примусово застосувати режим робочого столу"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Примусово застосувати експериментальний режим робочого столу на додаткових екранах"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Замінити примусовий темний режим"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Не дозволяє постійний примусовий темний режим"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Конфіденційність"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 918dda8..b357042 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"ہوائی جہاز وضع کے دوران غیر دستیاب"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"ڈیسک ٹاپ موڈ فورس کریں"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"ثانوی ڈسپلیز پر تجرباتی ڈیسک ٹاپ موڈ فورس کریں"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"force-dark کو اوور رائیڈ کریں"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"force-dark کی خصوصیت کو ہمیشہ زبردستی آن رکھنے کے لیے اوور رائیڈ کرتی ہے"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"رازداری"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index f6862c6..ad2b2c6 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Ekrandagi matnni kattalashtirish yoki kichiklashtirish."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Kichiklashtirish"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Kattalashtirish"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Namunaviy matn"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"Oz mamlakati sehrgari"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"11-bob: Oz mamlakatining zumrad shahri"</string>
@@ -4536,6 +4535,8 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Parvoz rejimida ishlamaydi"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Desktop rejimini majburiy ochish"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Ikkilamchi displeylarda tajribaviy desktop rejimini majburiy ochish"</string>
+ <string name="enable_sizecompat_freeform" msgid="3799755160777404309">"Oʻzgarmas hajmli ilovalarning erkin shakl olishi"</string>
+ <string name="enable_sizecompat_freeform_summary" msgid="1484050174538201499">"Oʻzgarmas hajmli ilovalarning erkin shaklda ochilishiga ruxsat beradi"</string>
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Tungi mavzuni yoqish ustidan yozadi"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Tungi mavzuni majburiy yoqish sozlamasini “Doimo yoniq” sifatida sozlaydi."</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Maxfiylik"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 7354940..4d3a549 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -4522,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Không sử dụng được khi ở chế độ trên máy bay"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Buộc chạy chế độ máy tính"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Buộc chạy chế độ máy tính thử nghiệm trên màn hình phụ"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Ghi đè tính năng buộc tối"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ghi đè tính năng buộc tối để luôn bật"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Quyền riêng tư"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index b20670d..0558544 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"在飞行模式下不可用"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"强制使用桌面模式"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"在辅助显示屏上强制使用实验桌面模式"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"覆盖“强制启用 SmartDark 功能”的设置"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"覆盖“强制 SmartDark 功能始终开启”的设置"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"隐私权"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3062134..944e461 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -4524,6 +4524,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"飛行模式啟用期間無法使用"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"強制執行桌面模式"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"在次要顯示屏強制執行實驗版桌面模式"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"蓋過強制深色模式"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"蓋過永遠啟用強制深色模式的設定"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"私隱"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index d7df3ef..656d202 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -4523,6 +4523,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"飛航模式開啟時無法使用"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"強制使用桌面模式"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"在次要顯示器上強制使用桌面模式實驗性功能"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"覆寫強制使用深色模式的功能"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"覆寫強制使用深色模式的功能,讓系統一律開啟這個模式"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"隱私"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 3e48c99..567440a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -51,8 +51,7 @@
<string name="font_size_summary" msgid="1296835853522566260">"Yenza umbhalo okusikrini ube mncane noma mkhulu."</string>
<string name="font_size_make_smaller_desc" msgid="4978038055549590140">"Yenza kube kuncane"</string>
<string name="font_size_make_larger_desc" msgid="5583046033381722247">"Yenza kube kukhulu"</string>
- <!-- no translation found for font_size_preview_text (5578792111916168343) -->
- <skip />
+ <string name="font_size_preview_text" msgid="5578792111916168343">"Servez à ce monsieur une bière et des kiwis."</string>
<string name="font_size_preview_text_headline" msgid="1173103737980511652">"Isampuli yombhalo"</string>
<string name="font_size_preview_text_title" msgid="6363561029914452382">"I-The Wonderful Wizard of Oz"</string>
<string name="font_size_preview_text_subtitle" msgid="5806349524325544614">"Isahluko 11: The Wonderful Emerald City of Oz"</string>
@@ -3398,8 +3397,7 @@
<string name="notification_assistant_title" msgid="6983941403582134437">"Izaziso eziguqukayo"</string>
<string name="no_notification_assistant" msgid="2533323397091834096">"Lutho"</string>
<string name="no_notification_listeners" msgid="2839354157349636000">"Azikho izinhlelo zokusebenza ezicele ukufinyelela kwesaziso."</string>
- <!-- no translation found for notification_access_detail_switch (46386786409608330) -->
- <skip />
+ <string name="notification_access_detail_switch" msgid="46386786409608330">"Vumela ukufinyelela kwesaziso"</string>
<string name="notification_assistant_security_warning_title" msgid="2972346436050925276">"Vumela ukufinyelela kwesaziso kwe-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="notification_assistant_security_warning_summary" msgid="3119843404577399036">"<xliff:g id="NOTIFICATION_ASSISTANT_NAME">%1$s</xliff:g> uzokwazi ukufunda zonke izaziso, kufaka phakathi ulwazi lomuntu siqu njengamagama okuxhumana nemibhalo yemilayezo oyitholayo. Futhi izokwazi ukushintsha noma ukulahla izaziso noma ukucupha izinkinobho zesenzo eziqukethwe. \n\nLokhu kuzonika uhlelo lokusebenza amandla okuvula nokuvala okuthi ungaphazamisi futhi kushintshe izilungiselelo eziphathelene."</string>
<string name="notification_listener_security_warning_title" msgid="5791700876622858363">"Vumela ukufinyelela kwe-<xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
@@ -4524,6 +4522,10 @@
<string name="mobile_data_ap_mode_disabled" msgid="6067959496888990983">"Ayitholakali ngesikhathi semodi yendiza"</string>
<string name="force_desktop_mode" msgid="1336913605091334238">"Phoqelela imodi yedeskithophu"</string>
<string name="force_desktop_mode_summary" msgid="4587416867846930479">"Phoqelela imodi yedeskithophu yokuhlola esibukisweni sesibili"</string>
+ <!-- no translation found for enable_sizecompat_freeform (3799755160777404309) -->
+ <skip />
+ <!-- no translation found for enable_sizecompat_freeform_summary (1484050174538201499) -->
+ <skip />
<string name="hwui_force_dark_title" msgid="4256904905631994219">"Bhala ngaphezulu isici sokuphoqelela okumnyama"</string>
<string name="hwui_force_dark_summary" msgid="6515748781487952769">"Ibhala ngaphezulu isici sokuphoqelela okumnyama ukuthi ihlale sivulekile"</string>
<string name="privacy_dashboard_title" msgid="6845403825611829558">"Ubumfihlo"</string>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 75205e9..068ba8c 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -100,6 +100,14 @@
<item>1800000</item>
</string-array>
+ <!-- Dark theme scheduling preferences [CHAR LIMIT=NONE] -->
+ <string-array name="dark_ui_scheduler_preference_titles">
+ <!-- 1: None -->
+ <item>@string/dark_ui_auto_mode_never</item>
+ <!-- 2: Auto -->
+ <item>@string/dark_ui_auto_mode_auto</item>
+ </string-array>
+
<!-- Security settings. The delay after screen is turned off until device locks.
These are shown in a list dialog. -->
<string-array name="lock_after_timeout_entries">
@@ -380,14 +388,12 @@
<!-- Phase 2 options for PEAP -->
<string-array name="wifi_peap_phase2_entries">
- <item>None</item>
<item>MSCHAPV2</item>
<item>GTC</item>
</string-array>
<!-- Type of EAP method when EAP SIM, AKA, AKA' are supported -->
<string-array name="wifi_peap_phase2_entries_with_sim_auth">
- <item>None</item>
<item translatable="false">MSCHAPV2</item>
<item translatable="false">GTC</item>
<item translatable="false">SIM</item>
@@ -395,9 +401,8 @@
<item translatable="false">AKA\'</item>
</string-array>
- <!-- Phase 2 options for rest of EAP methods -->
- <string-array name="wifi_phase2_entries">
- <item>None</item>
+ <!-- Phase 2 options for TTLS -->
+ <string-array name="wifi_ttls_phase2_entries">
<item>PAP</item>
<item>MSCHAP</item>
<item>MSCHAPV2</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index da66836..41c059c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -409,4 +409,10 @@
<!-- Battery meter icon size of advanced bluetooth device -->
<dimen name="advanced_bluetooth_battery_meter_width">7.8dp</dimen>
<dimen name="advanced_bluetooth_battery_meter_height">13dp</dimen>
+
+ <!-- Developer option bluetooth settings dialog -->
+ <dimen name="developer_option_dialog_margin_start">8dp</dimen>
+ <dimen name="developer_option_dialog_margin_top">8dp</dimen>
+ <dimen name="developer_option_dialog_min_height">48dp</dimen>
+ <dimen name="developer_option_dialog_padding_start">16dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4242609..26a2afc 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2760,6 +2760,40 @@
<!-- Night display slice screen, subtitle of intensity setting when night light is off. [CHAR LIMIT=30] -->
<string name="night_display_not_currently_on">Night Light not currently on</string>
+ <!-- Dark ui screen-->
+ <!-- Display settings screen, activation button action for manual mode. [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_on_manual">Turn on now</string>
+ <!-- Display settings screen, deactivation button action for manual mode. [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_off_manual">Turn off now</string>
+ <!-- Display settings screen, activation button action for sunset-to-sunrise schedule [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_on_auto">Turn on until sunrise</string>
+ <!-- Display settings screen, deactivation button action for sunset-to-sunrise schedule [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_off_auto">Turn off until sunset</string>
+ <!-- Dark UI screen, setting option name to enable Dark UI [CHAR LIMIT=30] -->
+ <string name="dark_ui_title">Dark Mode</string>
+ <!-- Dark UI screen, setting option name to configure whether Dark UI turn on/off automatically. [CHAR LIMIT=30] -->
+ <string name="dark_ui_auto_mode_title">Schedule</string>
+ <!-- Dark UI screen, setting option value for Dark UI to *never* turn on/off automatically. [CHAR LIMIT=30] -->
+ <string name="dark_ui_auto_mode_never">None</string>
+ <!-- Dark UIscreen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
+ <string name="dark_ui_auto_mode_auto">Turns on from sunset to sunrise</string>
+ <!-- Dark UIscreen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
+ <string name="dark_ui_status_title">Status</string>
+ <!-- Display settings screen, summary format of Dark UI when off. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
+ <!-- Display settings screen, summary of Dark UI when off and will *never* turn on automatically. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_off_auto_mode_never">Will never turn on automatically</string>
+ <!-- Display settings screen, summary of Dark UI when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_off_auto_mode_auto">Will turn on automatically at sunset</string>
+ <!-- Display settings screen, summary format of Dark UI when on. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_on">On / <xliff:g name="auto_mode_summary" example="Never turn off automatically">%1$s</xliff:g></string>
+ <!-- Display settings screen, summary of Dark UI when on and will *never* turn off automatically. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_on_auto_mode_never">Will never turn off automatically</string>
+ <!-- Display settings screen, summary of Dark UI when on and will turn off automatically at sunrise. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_on_auto_mode_auto">Will turn off automatically at sunrise</string>
+ <!-- Dark theme screen, description of Dark theme feature. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_text">Dark theme uses true black to help keep your battery alive longer. Dark theme schedules wait to turn on until your screen is off.</string>
+
<!-- Sound & display settings screen, setting option name to change screen timeout -->
<string name="screen_timeout">Screen timeout</string>
<!-- Sound & display settings screen, setting option name to change screen timeout [CHAR LIMIT=30] -->
@@ -6877,6 +6911,7 @@
<string name="help_url_connected_devices" translatable="false"></string>
<string name="help_url_apps_and_notifications" translatable="false"></string>
<string name="help_url_night_display" translatable="false"></string>
+ <string name="help_url_dark_theme" translatable="false"></string>
<string name="help_url_screen_saver" translatable="false"></string>
<string name="help_url_pickup_gesture" translatable="false"></string>
<string name="help_url_storage_dashboard" translatable="false"></string>
@@ -9969,6 +10004,12 @@
<!-- [CHAR_LIMIT=40] Positive button text in dark theme notification -->
<string name="dark_ui_settings_dialog_acknowledge">Got it</string>
+ <!-- [CHAR_LIMIT=50] Title string in the dark theme slice(suggestion) -->
+ <string name="dark_theme_slice_title">Try Dark theme</string>
+
+ <!-- [CHAR_LIMIT=50] Subtitle string in the dark theme slice(suggestion) -->
+ <string name="dark_theme_slice_subtitle">Helps extend battery life</string>
+
<!-- [CHAR LIMIT=60] Name of dev option to enable extra quick settings tiles -->
<string name="quick_settings_developer_tiles">Quick settings developer tiles</string>
diff --git a/res/xml/dark_mode_settings.xml b/res/xml/dark_mode_settings.xml
new file mode 100644
index 0000000..9247a0c
--- /dev/null
+++ b/res/xml/dark_mode_settings.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/dark_ui_mode"
+ android:key="dark_ui_mode_screen"
+ settings:keywords="@string/keywords_dark_ui_mode">
+
+ <DropDownPreference
+ android:key="dark_ui_auto_mode"
+ android:title="@string/dark_ui_auto_mode_title"
+ android:summary="%s"
+ android:entries="@array/dark_ui_scheduler_preference_titles"
+ android:entryValues="@array/dark_ui_scheduler_preference_titles"
+ settings:controller="com.android.settings.display.darkmode.DarkModeScheduleSelectorController"
+ settings:keywords="@string/keywords_dark_ui_mode"/>
+
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="dark_ui_activated"
+ android:title="@string/dark_ui_title"
+ android:selectable="false"
+ android:layout="@layout/dark_ui_activation_button"
+ settings:allowDividerBelow="true"
+ settings:controller="com.android.settings.display.darkmode.DarkModeActivationPreferenceController"
+ settings:keywords="@string/keywords_dark_ui_mode"/>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="dark_ui_footer"
+ android:title="@string/dark_ui_text"
+ android:selectable="false"
+ settings:searchable="false"
+ settings:allowDividerAbove="true"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/dark_ui_settings.xml b/res/xml/dark_ui_settings.xml
deleted file mode 100644
index 1f11eba..0000000
--- a/res/xml/dark_ui_settings.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:title="@string/dark_ui_mode"
- android:key="dark_ui_mode_screen"
- settings:staticPreferenceLocation="append"
- settings:keywords="@string/keywords_dark_ui_mode">
-</PreferenceScreen >
diff --git a/res/xml/display_settings.xml b/res/xml/display_settings.xml
index c45e6a2..9bc6710 100644
--- a/res/xml/display_settings.xml
+++ b/res/xml/display_settings.xml
@@ -31,6 +31,15 @@
<intent android:action="com.android.intent.action.SHOW_BRIGHTNESS_DIALOG" />
</com.android.settingslib.RestrictedPreference>
+ <com.android.settings.display.darkmode.DarkModePreference
+ android:key="dark_ui_mode"
+ android:title="@string/dark_ui_mode"
+ android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"
+ android:widgetLayout="@null"
+ settings:widgetLayout="@null"
+ settings:controller="com.android.settings.display.DarkUIPreferenceController"
+ settings:searchable="false"/>
+
<com.android.settings.display.NightDisplayPreference
android:key="night_display"
android:title="@string/night_display_title"
@@ -54,13 +63,6 @@
settings:controller="com.android.settings.display.WallpaperPreferenceController">
</com.android.settingslib.RestrictedPreference>
-
- <SwitchPreference
- android:key="dark_ui_mode"
- android:title="@string/dark_ui_mode"
- settings:keywords="@string/keywords_dark_ui_mode"
- settings:controller="com.android.settings.display.DarkUIPreferenceController"/>
-
<!-- Cross-listed item, if you change this, also change it in power_usage_summary.xml -->
<com.android.settings.display.TimeoutListPreference
android:key="screen_timeout"
diff --git a/res/xml/inactive_apps.xml b/res/xml/dummy_preference_screen.xml
similarity index 85%
rename from res/xml/inactive_apps.xml
rename to res/xml/dummy_preference_screen.xml
index 6f93bdb..4ffa916 100644
--- a/res/xml/inactive_apps.xml
+++ b/res/xml/dummy_preference_screen.xml
@@ -15,8 +15,7 @@
-->
<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:title="@string/inactive_apps_title">
-
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ settings:searchable="false">
</PreferenceScreen>
diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml
index 63ce90b..66447cd 100644
--- a/res/xml/storage_dashboard_fragment.xml
+++ b/res/xml/storage_dashboard_fragment.xml
@@ -22,7 +22,9 @@
android:orderingFromXml="false">
<com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference
android:key="pref_summary"
- android:order="0" />
+ android:order="0"
+ settings:searchable="false"
+ settings:controller="com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController"/>
<com.android.settings.widget.MasterSwitchPreference
android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
android:key="toggle_asm"
diff --git a/res/xml/storage_summary_donut.xml b/res/xml/storage_summary_donut.xml
new file mode 100644
index 0000000..be95cf8
--- /dev/null
+++ b/res/xml/storage_summary_donut.xml
@@ -0,0 +1,28 @@
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="storage_dashboard_screen"
+ android:title="@string/storage_settings"
+ android:orderingFromXml="false">
+ <com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference
+ android:key="pref_summary"
+ android:order="0"
+ settings:searchable="false"
+ settings:controller="com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController"/>
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/wifi_display_saved_access_points2.xml b/res/xml/wifi_display_saved_access_points2.xml
new file mode 100644
index 0000000..02c732a
--- /dev/null
+++ b/res/xml/wifi_display_saved_access_points2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:key="saved_access_points"
+ android:title="@string/wifi_saved_access_points_label">
+
+ <PreferenceCategory
+ android:key="subscribed_access_points_category"
+ android:title="@string/wifi_subscribed_access_points_tab"
+ settings:controller="com.android.settings.wifi.savedaccesspoints2.SubscribedAccessPointsPreferenceController2"/>
+
+ <PreferenceCategory
+ android:key="saved_access_points_category"
+ android:title="@string/wifi_saved_access_points_tab"
+ settings:controller="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsPreferenceController2"/>
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_settings2.xml b/res/xml/wifi_settings2.xml
new file mode 100644
index 0000000..8cd3857
--- /dev/null
+++ b/res/xml/wifi_settings2.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/wifi_settings"
+ settings:keywords="@string/keywords_wifi">
+
+ <com.android.settings.wifi.LinkablePreference
+ android:key="wifi_status_message"/>
+
+ <PreferenceCategory
+ android:key="connected_access_point"
+ android:layout="@layout/preference_category_no_label"/>
+
+ <PreferenceCategory
+ android:key="access_points"
+ android:layout="@layout/preference_category_no_label"/>
+
+ <Preference
+ android:key="configure_settings"
+ android:title="@string/wifi_configure_settings_preference_title"
+ settings:allowDividerAbove="true"
+ android:fragment="com.android.settings.wifi.ConfigureWifiSettings"/>
+
+ <Preference
+ android:key="saved_networks"
+ android:title="@string/wifi_saved_access_points_label"
+ android:fragment="com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2"/>
+
+ <com.android.settings.datausage.DataUsagePreference
+ android:key="wifi_data_usage"
+ android:title="@string/wifi_data_usage"/>
+</PreferenceScreen>
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 29ac8b6..6b23b29 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -23,7 +23,6 @@
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.display.BrightnessLevelPreferenceController;
import com.android.settings.display.CameraGesturePreferenceController;
-import com.android.settings.display.DarkUIPreferenceController;
import com.android.settings.display.LiftToWakePreferenceController;
import com.android.settings.display.NightDisplayPreferenceController;
import com.android.settings.display.NightModePreferenceController;
@@ -65,7 +64,6 @@
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
- use(DarkUIPreferenceController.class).setParentFragment(this);
}
@Override
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index e459ab8..70536c0 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -583,12 +583,7 @@
// Generally the items that are will be changing from these updates will
// not be in the top list of tiles, so run it in the background and the
// SettingsBaseActivity will pick up on the updates automatically.
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- doUpdateTilesList();
- }
- });
+ AsyncTask.execute(() -> doUpdateTilesList());
}
private void doUpdateTilesList() {
@@ -648,7 +643,6 @@
|| somethingChanged;
if (UserHandle.MU_ENABLED && !isAdmin) {
-
// When on restricted users, disable all extra categories (but only the settings ones).
final List<DashboardCategory> categories = mDashboardFeatureProvider.getAllCategories();
synchronized (categories) {
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 65d0c82..b5a1539 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -21,9 +21,6 @@
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
-import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_PERSONAL_ONLY;
-import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_WORK_ONLY;
-
import android.annotation.Nullable;
import android.app.ActionBar;
import android.app.Activity;
@@ -106,6 +103,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.widget.ActionBarShadowController;
@@ -1061,12 +1059,14 @@
*/
public static Fragment getTargetFragment(Activity activity, String fragmentName, Bundle args) {
Fragment f = null;
- final boolean isWorkOnly = args == null ? false : args.getBoolean(EXTRA_WORK_ONLY);
- final boolean isPersonalOnly = args == null ? false : args.getBoolean(EXTRA_PERSONAL_ONLY);
+ final boolean isPersonal = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
+ == ProfileSelectFragment.PERSONAL : false;
+ final boolean isWork = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
+ == ProfileSelectFragment.WORK : false;
if (FeatureFlagUtils.isEnabled(activity, FeatureFlags.PERSONAL_WORK_PROFILE)
&& UserManager.get(activity).getUserProfiles().size() > 1
&& ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName) != null
- && !isWorkOnly && !isPersonalOnly) {
+ && !isWork && !isPersonal) {
f = Fragment.instantiate(activity, ProfileFragmentBridge.FRAGMENT_MAP.get(fragmentName),
args);
} else {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 01289f2..1906a2d 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -29,6 +29,7 @@
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_POWER_WHITELIST_ALL;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_RECENT;
import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_WORK;
+import static com.android.settings.search.actionbar.SearchMenuController.MENU_SEARCH;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -99,6 +100,7 @@
import com.android.settings.applications.appinfo.WriteSettingsDetails;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.fuelgauge.HighPowerDetail;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
@@ -141,9 +143,7 @@
public static final String EXTRA_VOLUME_UUID = "volumeUuid";
public static final String EXTRA_VOLUME_NAME = "volumeName";
public static final String EXTRA_STORAGE_TYPE = "storageType";
- public static final String EXTRA_WORK_ONLY = "workProfileOnly";
public static final String EXTRA_WORK_ID = "workId";
- public static final String EXTRA_PERSONAL_ONLY = "personalOnly";
private static final String EXTRA_SORT_ORDER = "sortOrder";
private static final String EXTRA_SHOW_SYSTEM = "showSystem";
@@ -310,8 +310,10 @@
}
final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
mFilter = appFilterRegistry.get(appFilterRegistry.getDefaultFilterType(mListType));
- mIsPersonalOnly = args != null ? args.getBoolean(EXTRA_PERSONAL_ONLY) : false;
- mIsWorkOnly = args != null ? args.getBoolean(EXTRA_WORK_ONLY) : false;
+ mIsPersonalOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
+ == ProfileSelectFragment.PERSONAL : false;
+ mIsWorkOnly = args != null ? args.getInt(ProfileSelectFragment.EXTRA_PROFILE)
+ == ProfileSelectFragment.WORK : false;
mWorkUserId = args != null ? args.getInt(EXTRA_WORK_ID) : NO_USER_SPECIFIED;
mExpandSearch = activity.getIntent().getBooleanExtra(EXTRA_EXPAND_SEARCH_VIEW, false);
@@ -696,6 +698,10 @@
// Hide notification menu items, because sorting happens when filtering
mOptionsMenu.findItem(R.id.sort_order_recent_notification).setVisible(false);
mOptionsMenu.findItem(R.id.sort_order_frequent_notification).setVisible(false);
+ final MenuItem searchItem = mOptionsMenu.findItem(MENU_SEARCH);
+ if (searchItem != null) {
+ searchItem.setVisible(false);
+ }
}
@Override
diff --git a/src/com/android/settings/core/BasePreferenceController.java b/src/com/android/settings/core/BasePreferenceController.java
index cef372e..eeb77ad 100644
--- a/src/com/android/settings/core/BasePreferenceController.java
+++ b/src/com/android/settings/core/BasePreferenceController.java
@@ -275,6 +275,14 @@
}
/**
+ * Updates dynamic raw data for search provider.
+ *
+ * Called by SearchIndexProvider#getDynamicRawDataToIndex
+ */
+ public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
+ }
+
+ /**
* Set {@link UiBlockListener}
*
* @param uiBlockListener listener to set
@@ -308,4 +316,4 @@
*/
public interface UiBlocker {
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/core/PreferenceControllerMixin.java b/src/com/android/settings/core/PreferenceControllerMixin.java
index fa61552..6ddea3d 100644
--- a/src/com/android/settings/core/PreferenceControllerMixin.java
+++ b/src/com/android/settings/core/PreferenceControllerMixin.java
@@ -18,8 +18,8 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.SearchIndexableRaw;
import java.util.List;
@@ -62,4 +62,12 @@
*/
default void updateRawDataToIndex(List<SearchIndexableRaw> rawData) {
}
+
+ /**
+ * Updates dynamic raw data for search provider.
+ *
+ * Called by SearchIndexProvider#getDynamicRawDataToIndex
+ */
+ default void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
+ }
}
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 0934ba9..98c6b87 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -146,6 +146,7 @@
import com.android.settings.wifi.calling.WifiCallingSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
+import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
import com.android.settings.wifi.tether.WifiTetherSettings;
public class SettingsGateway {
@@ -161,6 +162,7 @@
WifiSettings2.class.getName(),
ConfigureWifiSettings.class.getName(),
SavedAccessPointsWifiSettings.class.getName(),
+ SavedAccessPointsWifiSettings2.class.getName(),
TetherSettings.class.getName(),
WifiP2pSettings.class.getName(),
WifiTetherSettings.class.getName(),
diff --git a/src/com/android/settings/dashboard/CategoryManager.java b/src/com/android/settings/dashboard/CategoryManager.java
index 5cc75c8..525b6f8 100644
--- a/src/com/android/settings/dashboard/CategoryManager.java
+++ b/src/com/android/settings/dashboard/CategoryManager.java
@@ -27,6 +27,7 @@
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
@@ -189,21 +190,31 @@
/**
* Filter out duplicate tiles from category. Duplicate tiles are the ones pointing to the
- * same intent.
+ * same intent for ActivityTile, and also the ones having the same description for ProviderTile.
*/
@VisibleForTesting
synchronized void filterDuplicateTiles(Map<String, DashboardCategory> categoryByKeyMap) {
for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
final DashboardCategory category = categoryEntry.getValue();
final int count = category.getTilesCount();
+ final Set<String> descriptions = new ArraySet<>();
final Set<ComponentName> components = new ArraySet<>();
for (int i = count - 1; i >= 0; i--) {
final Tile tile = category.getTile(i);
- final ComponentName tileComponent = tile.getIntent().getComponent();
- if (components.contains(tileComponent)) {
- category.removeTile(i);
+ if (tile instanceof ProviderTile) {
+ final String desc = tile.getDescription();
+ if (descriptions.contains(desc)) {
+ category.removeTile(i);
+ } else {
+ descriptions.add(desc);
+ }
} else {
- components.add(tileComponent);
+ final ComponentName tileComponent = tile.getIntent().getComponent();
+ if (components.contains(tileComponent)) {
+ category.removeTile(i);
+ } else {
+ components.add(tileComponent);
+ }
}
}
}
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProvider.java b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
index 9f562a0..8c872f0 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProvider.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProvider.java
@@ -44,19 +44,22 @@
String getDashboardKeyForTile(Tile tile);
/**
- * Binds preference to data provided by tile.
+ * Binds preference to data provided by tile and gets dynamic data observers.
*
* @param activity If tile contains intent to launch, it will be launched from this activity
- * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to rounded icon.
+ * @param forceRoundedIcon Whether or not injected tiles from other packages should be forced to
+ * rounded icon.
* @param sourceMetricsCategory The context (source) from which an action is performed
* @param pref The preference to bind data
* @param tile The binding data
* @param key They key for preference. If null, we will generate one from tile data
* @param baseOrder The order offset value. When binding, pref's order is determined by
* both this value and tile's own priority.
+ * @return The list of dynamic data observers
*/
- void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
- int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder);
+ List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
+ boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
+ String key, int baseOrder);
/**
* Opens a tile to its destination intent.
diff --git a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
index 2cfcbe6..4e541cb 100644
--- a/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
+++ b/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
@@ -18,9 +18,18 @@
import static android.content.Intent.EXTRA_USER;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_CHECKED_STATE;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_SUMMARY;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_DYNAMIC_TITLE;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_GET_PROVIDER_ICON;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_IS_CHECKED;
+import static com.android.settingslib.drawer.SwitchesProvider.METHOD_ON_CHECKED_CHANGED;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_ICON_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY_URI;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE_URI;
@@ -40,22 +49,26 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
+import android.widget.Toast;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.DashboardCategory;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AdaptiveIcon;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -106,40 +119,54 @@
}
@Override
- public void bindPreferenceToTile(FragmentActivity activity, boolean forceRoundedIcon,
- int sourceMetricsCategory, Preference pref, Tile tile, String key, int baseOrder) {
+ public List<DynamicDataObserver> bindPreferenceToTileAndGetObservers(FragmentActivity activity,
+ boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile tile,
+ String key, int baseOrder) {
if (pref == null) {
- return;
+ return null;
}
if (!TextUtils.isEmpty(key)) {
pref.setKey(key);
} else {
pref.setKey(getDashboardKeyForTile(tile));
}
- bindTitle(pref, tile);
- bindSummary(pref, tile);
+ final List<DynamicDataObserver> outObservers = new ArrayList<>();
+ DynamicDataObserver observer = bindTitleAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
+ observer = bindSummaryAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
+ observer = bindSwitchAndGetObserver(pref, tile);
+ if (observer != null) {
+ outObservers.add(observer);
+ }
bindIcon(pref, tile, forceRoundedIcon);
- final Bundle metadata = tile.getMetaData();
- String clsName = null;
- String action = null;
- if (metadata != null) {
- clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
- action = metadata.getString(META_DATA_KEY_INTENT_ACTION);
- }
- if (!TextUtils.isEmpty(clsName)) {
- pref.setFragment(clsName);
- } else {
- final Intent intent = new Intent(tile.getIntent());
- intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
- sourceMetricsCategory);
- if (action != null) {
- intent.setAction(action);
+ if (tile instanceof ActivityTile) {
+ final Bundle metadata = tile.getMetaData();
+ String clsName = null;
+ String action = null;
+ if (metadata != null) {
+ clsName = metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);
+ action = metadata.getString(META_DATA_KEY_INTENT_ACTION);
}
- pref.setOnPreferenceClickListener(preference -> {
- launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory);
- return true;
- });
+ if (!TextUtils.isEmpty(clsName)) {
+ pref.setFragment(clsName);
+ } else {
+ final Intent intent = new Intent(tile.getIntent());
+ intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
+ sourceMetricsCategory);
+ if (action != null) {
+ intent.setAction(action);
+ }
+ pref.setOnPreferenceClickListener(preference -> {
+ launchIntentOrSelectProfile(activity, tile, intent, sourceMetricsCategory);
+ return true;
+ });
+ }
}
if (tile.hasOrder()) {
@@ -153,6 +180,7 @@
pref.setOrder(order + baseOrder);
}
}
+ return outObservers.isEmpty() ? null : outObservers;
}
@Override
@@ -170,11 +198,35 @@
launchIntentOrSelectProfile(activity, tile, intent, SettingsEnums.DASHBOARD_SUMMARY);
}
- private void bindTitle(Preference preference, Tile tile) {
+ private DynamicDataObserver createDynamicDataObserver(String method, Uri uri, Preference pref) {
+ return new DynamicDataObserver() {
+ @Override
+ public Uri getUri() {
+ return uri;
+ }
+
+ @Override
+ public void onDataChanged() {
+ switch (method) {
+ case METHOD_GET_DYNAMIC_TITLE:
+ refreshTitle(uri, pref);
+ break;
+ case METHOD_GET_DYNAMIC_SUMMARY:
+ refreshSummary(uri, pref);
+ break;
+ case METHOD_IS_CHECKED:
+ refreshSwitch(uri, pref);
+ break;
+ }
+ }
+ };
+ }
+
+ private DynamicDataObserver bindTitleAndGetObserver(Preference preference, Tile tile) {
final CharSequence title = tile.getTitle(mContext.getApplicationContext());
if (title != null) {
preference.setTitle(title);
- return;
+ return null;
}
if (tile.getMetaData() != null && tile.getMetaData().containsKey(
META_DATA_PREFERENCE_TITLE_URI)) {
@@ -182,9 +234,12 @@
// to avoid preference height change.
preference.setTitle(R.string.summary_placeholder);
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_TITLE_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_TITLE_URI,
+ METHOD_GET_DYNAMIC_TITLE);
refreshTitle(uri, preference);
+ return createDynamicDataObserver(METHOD_GET_DYNAMIC_TITLE, uri, preference);
}
+ return null;
}
private void refreshTitle(Uri uri, Preference preference) {
@@ -196,7 +251,7 @@
});
}
- private void bindSummary(Preference preference, Tile tile) {
+ private DynamicDataObserver bindSummaryAndGetObserver(Preference preference, Tile tile) {
final CharSequence summary = tile.getSummary(mContext);
if (summary != null) {
preference.setSummary(summary);
@@ -206,11 +261,14 @@
// to avoid preference height change.
preference.setSummary(R.string.summary_placeholder);
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SUMMARY_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SUMMARY_URI,
+ METHOD_GET_DYNAMIC_SUMMARY);
refreshSummary(uri, preference);
+ return createDynamicDataObserver(METHOD_GET_DYNAMIC_SUMMARY, uri, preference);
} else {
preference.setSummary(R.string.summary_placeholder);
}
+ return null;
}
private void refreshSummary(Uri uri, Preference preference) {
@@ -222,6 +280,70 @@
});
}
+ private DynamicDataObserver bindSwitchAndGetObserver(Preference preference, Tile tile) {
+ if (!tile.hasSwitch()) {
+ return null;
+ }
+
+ final Uri onCheckedChangedUri = TileUtils.getCompleteUri(tile,
+ META_DATA_PREFERENCE_SWITCH_URI, METHOD_ON_CHECKED_CHANGED);
+ preference.setOnPreferenceChangeListener((pref, newValue) -> {
+ onCheckedChanged(onCheckedChangedUri, pref, (boolean) newValue);
+ return true;
+ });
+
+ final Uri isCheckedUri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_SWITCH_URI,
+ METHOD_IS_CHECKED);
+ setSwitchEnabled(preference, false);
+ refreshSwitch(isCheckedUri, preference);
+ return createDynamicDataObserver(METHOD_IS_CHECKED, isCheckedUri, preference);
+ }
+
+ private void onCheckedChanged(Uri uri, Preference pref, boolean checked) {
+ setSwitchEnabled(pref, false);
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Map<String, IContentProvider> providerMap = new ArrayMap<>();
+ final Bundle result = TileUtils.putBooleanToUri(mContext, uri, providerMap,
+ EXTRA_SWITCH_CHECKED_STATE, checked);
+
+ ThreadUtils.postOnMainThread(() -> {
+ setSwitchEnabled(pref, true);
+ final boolean error = result.getBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR);
+ if (!error) {
+ return;
+ }
+
+ setSwitchChecked(pref, !checked);
+ final String errorMsg = result.getString(EXTRA_SWITCH_SET_CHECKED_ERROR_MESSAGE);
+ if (!TextUtils.isEmpty(errorMsg)) {
+ Toast.makeText(mContext, errorMsg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ });
+ }
+
+ private void refreshSwitch(Uri uri, Preference preference) {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final Map<String, IContentProvider> providerMap = new ArrayMap<>();
+ final boolean checked = TileUtils.getBooleanFromUri(mContext, uri, providerMap,
+ EXTRA_SWITCH_CHECKED_STATE);
+ ThreadUtils.postOnMainThread(() -> {
+ setSwitchChecked(preference, checked);
+ setSwitchEnabled(preference, true);
+ });
+ });
+ }
+
+ private void setSwitchChecked(Preference pref, boolean checked) {
+ if (pref instanceof SwitchPreference) {
+ ((SwitchPreference) pref).setChecked(checked);
+ }
+ }
+
+ private void setSwitchEnabled(Preference pref, boolean enabled) {
+ pref.setEnabled(enabled);
+ }
+
@VisibleForTesting
void bindIcon(Preference preference, Tile tile, boolean forceRoundedIcon) {
// Use preference context instead here when get icon from Tile, as we are using the context
@@ -246,7 +368,8 @@
packageName = intent.getComponent().getPackageName();
}
final Map<String, IContentProvider> providerMap = new ArrayMap<>();
- final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_ICON_URI);
+ final Uri uri = TileUtils.getCompleteUri(tile, META_DATA_PREFERENCE_ICON_URI,
+ METHOD_GET_PROVIDER_ICON);
final Pair<String, Integer> iconInfo = TileUtils.getIconFromUri(
mContext, packageName, uri, providerMap);
if (iconInfo == null) {
diff --git a/src/com/android/settings/dashboard/DashboardFragment.java b/src/com/android/settings/dashboard/DashboardFragment.java
index 3575b72..30d6df3 100644
--- a/src/com/android/settings/dashboard/DashboardFragment.java
+++ b/src/com/android/settings/dashboard/DashboardFragment.java
@@ -17,11 +17,11 @@
import android.app.Activity;
import android.app.settings.SettingsEnums;
+import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.CallSuper;
@@ -30,6 +30,7 @@
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
@@ -41,6 +42,7 @@
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.search.Indexable;
@@ -49,7 +51,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.Set;
+import java.util.Objects;
/**
* Base fragment for dashboard style UI containing a list of static and dynamic setting items.
@@ -60,9 +62,11 @@
BasePreferenceController.UiBlockListener {
private static final String TAG = "DashboardFragment";
+ @VisibleForTesting
+ final ArrayMap<String, List<DynamicDataObserver>> mDashboardTilePrefKeys = new ArrayMap<>();
private final Map<Class, List<AbstractPreferenceController>> mPreferenceControllers =
new ArrayMap<>();
- private final Set<String> mDashboardTilePrefKeys = new ArraySet<>();
+ private final List<DynamicDataObserver> mRegisteredObservers = new ArrayList<>();
private final List<AbstractPreferenceController> mControllers = new ArrayList<>();
private DashboardFeatureProvider mDashboardFeatureProvider;
@@ -171,6 +175,15 @@
mListeningToCategoryChange = true;
((SettingsBaseActivity) activity).addCategoryListener(this);
}
+ final ContentResolver resolver = getContentResolver();
+ mDashboardTilePrefKeys.values().stream()
+ .filter(Objects::nonNull)
+ .flatMap(List::stream)
+ .forEach(observer -> {
+ if (!mRegisteredObservers.contains(observer)) {
+ registerDynamicDataObserver(resolver, observer);
+ }
+ });
}
@Override
@@ -200,6 +213,7 @@
@Override
public void onStop() {
super.onStop();
+ unregisterDynamicDataObservers(new ArrayList<>(mRegisteredObservers));
if (mListeningToCategoryChange) {
final Activity activity = getActivity();
if (activity instanceof SettingsBaseActivity) {
@@ -325,7 +339,7 @@
* Refresh all preference items, including both static prefs from xml, and dynamic items from
* DashboardCategory.
*/
- private void refreshAllPreferences(final String TAG) {
+ private void refreshAllPreferences(final String tag) {
final PreferenceScreen screen = getPreferenceScreen();
// First remove old preferences.
if (screen != null) {
@@ -336,11 +350,11 @@
// Add resource based tiles.
displayResourceTiles();
- refreshDashboardTiles(TAG);
+ refreshDashboardTiles(tag);
final Activity activity = getActivity();
if (activity != null) {
- Log.d(TAG, "All preferences added, reporting fully drawn");
+ Log.d(tag, "All preferences added, reporting fully drawn");
activity.reportFullyDrawn();
}
@@ -371,59 +385,62 @@
/**
* Refresh preference items backed by DashboardCategory.
*/
- @VisibleForTesting
- void refreshDashboardTiles(final String TAG) {
+ private void refreshDashboardTiles(final String tag) {
final PreferenceScreen screen = getPreferenceScreen();
final DashboardCategory category =
mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
if (category == null) {
- Log.d(TAG, "NO dashboard tiles for " + TAG);
+ Log.d(tag, "NO dashboard tiles for " + tag);
return;
}
final List<Tile> tiles = category.getTiles();
if (tiles == null) {
- Log.d(TAG, "tile list is empty, skipping category " + category.key);
+ Log.d(tag, "tile list is empty, skipping category " + category.key);
return;
}
// Create a list to track which tiles are to be removed.
- final List<String> remove = new ArrayList<>(mDashboardTilePrefKeys);
+ final Map<String, List<DynamicDataObserver>> remove = new ArrayMap(mDashboardTilePrefKeys);
// Install dashboard tiles.
final boolean forceRoundedIcons = shouldForceRoundedIcon();
for (Tile tile : tiles) {
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);
if (TextUtils.isEmpty(key)) {
- Log.d(TAG, "tile does not contain a key, skipping " + tile);
+ Log.d(tag, "tile does not contain a key, skipping " + tile);
continue;
}
if (!displayTile(tile)) {
continue;
}
- if (mDashboardTilePrefKeys.contains(key)) {
+ if (mDashboardTilePrefKeys.containsKey(key)) {
// Have the key already, will rebind.
final Preference preference = screen.findPreference(key);
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
- getMetricsCategory(), preference, tile, key,
+ mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
+ forceRoundedIcons, getMetricsCategory(), preference, tile, key,
mPlaceholderPreferenceController.getOrder());
} else {
// Don't have this key, add it.
- final Preference pref = new Preference(getPrefContext());
- mDashboardFeatureProvider.bindPreferenceToTile(getActivity(), forceRoundedIcons,
- getMetricsCategory(), pref, tile, key,
- mPlaceholderPreferenceController.getOrder());
+ final Preference pref = createPreference(tile);
+ final List<DynamicDataObserver> observers =
+ mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),
+ forceRoundedIcons, getMetricsCategory(), pref, tile, key,
+ mPlaceholderPreferenceController.getOrder());
screen.addPreference(pref);
- mDashboardTilePrefKeys.add(key);
+ registerDynamicDataObservers(observers);
+ mDashboardTilePrefKeys.put(key, observers);
}
remove.remove(key);
}
// Finally remove tiles that are gone.
- for (String key : remove) {
+ for (Map.Entry<String, List<DynamicDataObserver>> entry : remove.entrySet()) {
+ final String key = entry.getKey();
mDashboardTilePrefKeys.remove(key);
final Preference preference = screen.findPreference(key);
if (preference != null) {
screen.removePreference(preference);
}
+ unregisterDynamicDataObservers(entry.getValue());
}
}
@@ -431,4 +448,41 @@
public void onBlockerWorkFinished(BasePreferenceController controller) {
mBlockerController.countDown(controller.getPreferenceKey());
}
+
+ @VisibleForTesting
+ Preference createPreference(Tile tile) {
+ return tile instanceof ProviderTile
+ ? new SwitchPreference(getPrefContext())
+ : new Preference(getPrefContext());
+ }
+
+ @VisibleForTesting
+ void registerDynamicDataObservers(List<DynamicDataObserver> observers) {
+ if (observers == null || observers.isEmpty()) {
+ return;
+ }
+ final ContentResolver resolver = getContentResolver();
+ observers.forEach(observer -> registerDynamicDataObserver(resolver, observer));
+ }
+
+ private void registerDynamicDataObserver(ContentResolver resolver,
+ DynamicDataObserver observer) {
+ Log.d(TAG, "register observer: @" + Integer.toHexString(observer.hashCode())
+ + ", uri: " + observer.getUri());
+ resolver.registerContentObserver(observer.getUri(), false, observer);
+ mRegisteredObservers.add(observer);
+ }
+
+ private void unregisterDynamicDataObservers(List<DynamicDataObserver> observers) {
+ if (observers == null || observers.isEmpty()) {
+ return;
+ }
+ final ContentResolver resolver = getContentResolver();
+ observers.forEach(observer -> {
+ Log.d(TAG, "unregister observer: @" + Integer.toHexString(observer.hashCode())
+ + ", uri: " + observer.getUri());
+ mRegisteredObservers.remove(observer);
+ resolver.unregisterContentObserver(observer);
+ });
+ }
}
diff --git a/src/com/android/settings/dashboard/DynamicDataObserver.java b/src/com/android/settings/dashboard/DynamicDataObserver.java
new file mode 100644
index 0000000..f5299be
--- /dev/null
+++ b/src/com/android/settings/dashboard/DynamicDataObserver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.dashboard;
+
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+
+/**
+ * Observer for updating injected dynamic data.
+ */
+public abstract class DynamicDataObserver extends ContentObserver {
+
+ protected DynamicDataObserver() {
+ super(new Handler(Looper.getMainLooper()));
+ }
+
+ /** Returns the uri of the callback. */
+ public abstract Uri getUri();
+
+ /** Called when data changes. */
+ public abstract void onDataChanged();
+
+ @Override
+ public void onChange(boolean selfChange) {
+ onDataChanged();
+ }
+}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
index b7a1301..b8d4be1 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectFragment.java
@@ -22,13 +22,15 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.android.settings.R;
-import com.android.settings.core.InstrumentedFragment;
+import com.android.settings.dashboard.DashboardFragment;
import com.google.android.material.tabs.TabLayout;
@@ -38,7 +40,9 @@
/**
* Base fragment class for profile settings.
*/
-public abstract class ProfileSelectFragment extends InstrumentedFragment {
+public abstract class ProfileSelectFragment extends DashboardFragment {
+
+ private static final String TAG = "ProfileSelectFragment";
/**
* Denotes the profile type.
@@ -63,16 +67,29 @@
*/
public static final int ALL = PERSONAL | WORK;
- private View mContentView;
+ /**
+ * Used in fragment argument and pass {@link ProfileType} to it
+ */
+ public static final String EXTRA_PROFILE = "profile";
+
+ private ViewGroup mContentView;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- mContentView = inflater.inflate(R.layout.profile_select_tablayout, null /* root */);
- final ViewPager viewPager = mContentView.findViewById(R.id.view_pager);
- viewPager.setAdapter(new ViewPagerAdapter(this));
- final TabLayout tabs = mContentView.findViewById(R.id.tabs);
+ mContentView = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
+
+ final View tabContainer = mContentView.findViewById(R.id.tab_container);
+ final ViewPager viewPager = tabContainer.findViewById(R.id.view_pager);
+ viewPager.setAdapter(new ProfileSelectFragment.ViewPagerAdapter(this));
+ final TabLayout tabs = tabContainer.findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
+ tabContainer.setVisibility(View.VISIBLE);
+
+ final FrameLayout listContainer = mContentView.findViewById(android.R.id.list_container);
+ listContainer.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
return mContentView;
}
@@ -87,13 +104,23 @@
*/
public abstract Fragment[] getFragments();
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.dummy_preference_screen;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
static class ViewPagerAdapter extends FragmentStatePagerAdapter {
private final Fragment[] mChildFragments;
private final Context mContext;
ViewPagerAdapter(ProfileSelectFragment fragment) {
- super(fragment.getActivity().getSupportFragmentManager());
+ super(fragment.getChildFragmentManager());
mContext = fragment.getContext();
mChildFragments = fragment.getFragments();
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
index 7290258..8a9e4f8 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectManageApplications.java
@@ -16,9 +16,6 @@
package com.android.settings.dashboard.profileselector;
-import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_PERSONAL_ONLY;
-import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_WORK_ONLY;
-
import android.os.Bundle;
import androidx.fragment.app.Fragment;
@@ -33,12 +30,12 @@
@Override
public Fragment[] getFragments() {
final Bundle workOnly = new Bundle();
- workOnly.putBoolean(EXTRA_WORK_ONLY, true);
+ workOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.WORK);
final Fragment workFragment = new ManageApplications();
workFragment.setArguments(workOnly);
final Bundle personalOnly = new Bundle();
- personalOnly.putBoolean(EXTRA_PERSONAL_ONLY, true);
+ personalOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.PERSONAL);
final Fragment personalFragment = new ManageApplications();
personalFragment.setArguments(personalOnly);
return new Fragment[]{
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
index c7e4fd8..c204ad4 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileSelectStorageFragment.java
@@ -16,13 +16,15 @@
package com.android.settings.dashboard.profileselector;
-import android.content.pm.UserInfo;
import android.os.Bundle;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.VolumeInfo;
import androidx.fragment.app.Fragment;
+import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.deviceinfo.StorageProfileFragment;
@@ -35,21 +37,16 @@
final Bundle storageBundle = new Bundle();
storageBundle.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
+ storageBundle.putInt(EXTRA_PROFILE, ProfileSelectFragment.PERSONAL);
final Fragment storageDashboardFragment = new StorageDashboardFragment();
storageDashboardFragment.setArguments(storageBundle);
- UserInfo targetUser = null;
- for (UserInfo info : UserManager.get(getActivity()).getUsers()) {
- if (!info.isPrimary()) {
- targetUser = info;
- break;
- }
+ final UserHandle userHandle = Utils.getManagedProfile(UserManager.get(getActivity()));
+ if (userHandle != null) {
+ storageBundle.putInt(StorageProfileFragment.USER_ID_EXTRA, userHandle.getIdentifier());
}
- // TODO(b/143330969): Need to think about more profile users case
- if (targetUser != null) {
- storageBundle.putInt(StorageProfileFragment.USER_ID_EXTRA, targetUser.id);
- }
+
final Fragment storageProfileFragment = new StorageProfileFragment();
storageProfileFragment.setArguments(storageBundle);
@@ -58,5 +55,10 @@
storageProfileFragment
};
}
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.storage_summary_donut;
+ }
}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 0e53333..b36ad43 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -33,16 +33,17 @@
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
+import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
-import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController;
import com.android.settings.deviceinfo.storage.UserIconLoader;
import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -62,6 +63,7 @@
implements
LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
private static final String TAG = "StorageDashboardFrag";
+ private static final String SUMMARY_PREF_KEY = "pref_summary";
private static final int STORAGE_JOB_ID = 0;
private static final int ICON_JOB_ID = 1;
private static final int VOLUME_SIZE_JOB_ID = 2;
@@ -71,10 +73,10 @@
private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
private CachedStorageValuesHelper mCachedStorageValuesHelper;
- private StorageSummaryDonutPreferenceController mSummaryController;
private StorageItemPreferenceController mPreferenceController;
private PrivateVolumeOptionMenuController mOptionMenuController;
private List<AbstractPreferenceController> mSecondaryUsers;
+ private boolean mPersonalOnly;
@Override
public void onCreate(Bundle icicle) {
@@ -84,12 +86,19 @@
final Activity activity = getActivity();
StorageManager sm = activity.getSystemService(StorageManager.class);
mVolume = Utils.maybeInitializeVolume(sm, getArguments());
+ mPersonalOnly = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
+ == ProfileSelectFragment.PERSONAL;
if (mVolume == null) {
activity.finish();
return;
}
-
initializeOptionsMenu(activity);
+ if (mPersonalOnly) {
+ final Preference summary = getPreferenceScreen().findPreference(SUMMARY_PREF_KEY);
+ if (summary != null) {
+ summary.setVisible(false);
+ }
+ }
}
@Override
@@ -119,7 +128,6 @@
null /* header view */)
.setRecyclerView(getListView(), getSettingsLifecycle())
.styleActionBar(activity);
-
}
@Override
@@ -140,7 +148,6 @@
boolean stopLoading = false;
if (mStorageInfo != null) {
long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
- mSummaryController.updateBytes(privateUsedBytes, mStorageInfo.totalBytes);
mPreferenceController.setVolume(mVolume);
mPreferenceController.setUsedSize(privateUsedBytes);
mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
@@ -187,8 +194,6 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
- mSummaryController = new StorageSummaryDonutPreferenceController(context);
- controllers.add(mSummaryController);
StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
@@ -241,7 +246,6 @@
final StorageManager sm = context.getSystemService(StorageManager.class);
final UserManager userManager = context.getSystemService(UserManager.class);
final List<AbstractPreferenceController> controllers = new ArrayList<>();
- controllers.add(new StorageSummaryDonutPreferenceController(context));
controllers.add(new StorageItemPreferenceController(context, null /* host */,
null /* volume */, new StorageManagerVolumeProvider(sm)));
controllers.addAll(SecondaryUserController.getSecondaryUserControllers(
diff --git a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
index f6a989f..6e471e4 100644
--- a/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
+++ b/src/com/android/settings/deviceinfo/storage/SecondaryUserController.java
@@ -190,7 +190,8 @@
}
}
- private static class NoSecondaryUserController extends AbstractPreferenceController implements
+ @VisibleForTesting
+ static class NoSecondaryUserController extends AbstractPreferenceController implements
PreferenceControllerMixin {
public NoSecondaryUserController(Context context) {
super(context);
@@ -214,6 +215,5 @@
public String getPreferenceKey() {
return null;
}
-
}
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 31898d1..26039fb 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -41,6 +41,7 @@
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.PrivateVolumeSettings.SystemInfoFragment;
import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.overlay.FeatureFactory;
@@ -392,14 +393,15 @@
private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.PERSONAL_WORK_PROFILE)) {
- final Bundle args = new Bundle(3 + additionalCapacity);
- args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
+ final Bundle args = new Bundle(2 + additionalCapacity);
+ args.putInt(ProfileSelectFragment.EXTRA_PROFILE,
+ mIsWorkProfile ? ProfileSelectFragment.WORK : ProfileSelectFragment.PERSONAL);
args.putInt(ManageApplications.EXTRA_WORK_ID, mUserId);
- args.putBoolean(ManageApplications.EXTRA_PERSONAL_ONLY, !mIsWorkProfile);
return args;
} else {
final Bundle args = new Bundle(2 + additionalCapacity);
- args.putBoolean(ManageApplications.EXTRA_WORK_ONLY, mIsWorkProfile);
+ args.putInt(ProfileSelectFragment.EXTRA_PROFILE,
+ mIsWorkProfile ? ProfileSelectFragment.WORK : ProfileSelectFragment.ALL);
args.putInt(ManageApplications.EXTRA_WORK_ID, mUserId);
return args;
}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java
index d450a2a..d8ee711 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceController.java
@@ -17,6 +17,7 @@
package com.android.settings.deviceinfo.storage;
import android.content.Context;
+import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.text.format.Formatter;
@@ -25,22 +26,29 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.text.NumberFormat;
/**
- * StorgaeSummaryPreferenceController updates the donut storage summary preference to have the
+ * SummaryPreferenceController updates the donut storage summary preference to have the
* correct sizes showing.
*/
-public class StorageSummaryDonutPreferenceController extends AbstractPreferenceController implements
- PreferenceControllerMixin {
+public class StorageSummaryDonutPreferenceController extends BasePreferenceController {
private long mUsedBytes;
private long mTotalBytes;
private StorageSummaryDonutPreference mSummary;
+ private final StorageManager mStorageManager;
+ private final StorageManagerVolumeProvider mStorageManagerVolumeProvider;
- public StorageSummaryDonutPreferenceController(Context context) {
- super(context);
+ public StorageSummaryDonutPreferenceController(Context context, String key) {
+ super(context, key);
+ mStorageManager = mContext.getSystemService(StorageManager.class);
+ mStorageManagerVolumeProvider = new StorageManagerVolumeProvider(mStorageManager);
}
/**
@@ -58,19 +66,31 @@
@Override
public void displayPreference(PreferenceScreen screen) {
- mSummary = screen.findPreference("pref_summary");
+ mSummary = screen.findPreference(getPreferenceKey());
mSummary.setEnabled(true);
+
+ ThreadUtils.postOnBackgroundThread(() -> {
+ final NumberFormat percentageFormat = NumberFormat.getPercentInstance();
+ final PrivateStorageInfo info = PrivateStorageInfo.getPrivateStorageInfo(
+ mStorageManagerVolumeProvider);
+ final double privateUsedBytes = info.totalBytes - info.freeBytes;
+ mTotalBytes = info.totalBytes;
+ mUsedBytes = info.totalBytes - info.freeBytes;
+
+ ThreadUtils.postOnMainThread(() -> {
+ updateState(mSummary);
+ });
+ });
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
- StorageSummaryDonutPreference summary = (StorageSummaryDonutPreference) preference;
- summary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes));
- summary.setSummary(mContext.getString(R.string.storage_volume_total,
+ mSummary.setTitle(convertUsedBytesToFormattedText(mContext, mUsedBytes));
+ mSummary.setSummary(mContext.getString(R.string.storage_volume_total,
Formatter.formatShortFileSize(mContext, mTotalBytes)));
- summary.setPercent(mUsedBytes, mTotalBytes);
- summary.setEnabled(true);
+ mSummary.setPercent(mUsedBytes, mTotalBytes);
+ mSummary.setEnabled(true);
}
/** Invalidates the data on the view and re-renders. */
@@ -81,13 +101,8 @@
}
@Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
- public String getPreferenceKey() {
- return "pref_summary";
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
}
/**
diff --git a/src/com/android/settings/display/DarkUIPreferenceController.java b/src/com/android/settings/display/DarkUIPreferenceController.java
index d3d30b5..20683c7 100644
--- a/src/com/android/settings/display/DarkUIPreferenceController.java
+++ b/src/com/android/settings/display/DarkUIPreferenceController.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Configuration;
import android.os.PowerManager;
import android.provider.Settings;
@@ -28,7 +29,6 @@
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
@@ -44,7 +44,7 @@
public static final int DIALOG_SEEN = 1;
@VisibleForTesting
- SwitchPreference mPreference;
+ Preference mPreference;
private UiModeManager mUiModeManager;
private PowerManager mPowerManager;
@@ -68,7 +68,8 @@
@Override
public boolean isChecked() {
- return mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES;
+ return (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
}
@Override
@@ -92,15 +93,12 @@
showDarkModeDialog();
return false;
}
- mUiModeManager.setNightMode(isChecked
- ? UiModeManager.MODE_NIGHT_YES
- : UiModeManager.MODE_NIGHT_NO);
- return true;
+ return mUiModeManager.setNightModeActivated(isChecked);
}
private void showDarkModeDialog() {
final DarkUIInfoDialogFragment frag = new DarkUIInfoDialogFragment();
- if (mFragment.getFragmentManager() != null) {
+ if (mFragment != null && mFragment.getFragmentManager() != null) {
frag.show(mFragment.getFragmentManager(), getClass().getName());
}
}
@@ -113,12 +111,10 @@
boolean isBatterySaver = isPowerSaveMode();
mPreference.setEnabled(!isBatterySaver);
if (isBatterySaver) {
- int stringId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES
+ int stringId = isChecked()
? R.string.dark_ui_mode_disabled_summary_dark_theme_on
: R.string.dark_ui_mode_disabled_summary_dark_theme_off;
mPreference.setSummary(mContext.getString(stringId));
- } else {
- mPreference.setSummary(null);
}
}
@@ -127,22 +123,17 @@
return mPowerManager.isPowerSaveMode();
}
-
- @VisibleForTesting
- void setUiModeManager(UiModeManager uiModeManager) {
- mUiModeManager = uiModeManager;
- }
-
- public void setParentFragment(Fragment fragment) {
- mFragment = fragment;
- }
-
@Override
public void onStart() {
mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
+ // used by AccessibilitySettings
+ public void setParentFragment(Fragment fragment) {
+ mFragment = fragment;
+ }
+
@Override
public void onStop() {
mContext.unregisterReceiver(mReceiver);
diff --git a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
new file mode 100644
index 0000000..98d4b14
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.View;
+import android.widget.Button;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ * Controller for activate/deactivate night mode button
+ */
+public class DarkModeActivationPreferenceController extends BasePreferenceController {
+ private final UiModeManager mUiModeManager;
+ private Button mTurnOffButton;
+ private Button mTurnOnButton;
+
+ public DarkModeActivationPreferenceController(Context context,
+ String preferenceKey) {
+ super(context, preferenceKey);
+
+ mUiModeManager = context.getSystemService(UiModeManager.class);
+ }
+
+ @Override
+ public final void updateState(Preference preference) {
+ final boolean active = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ updateNightMode(active);
+ }
+
+ private void updateNightMode(boolean active) {
+ final int autoMode = mUiModeManager.getNightMode();
+ String buttonText;
+
+ if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+ buttonText = mContext.getString(active
+ ? R.string.dark_ui_activation_off_auto
+ : R.string.dark_ui_activation_on_auto);
+ } else {
+ buttonText = mContext.getString(active
+ ? R.string.dark_ui_activation_off_manual
+ : R.string.dark_ui_activation_on_manual);
+ }
+ if (active) {
+ mTurnOnButton.setVisibility(View.GONE);
+ mTurnOffButton.setVisibility(View.VISIBLE);
+ mTurnOffButton.setText(buttonText);
+ } else {
+ mTurnOnButton.setVisibility(View.VISIBLE);
+ mTurnOffButton.setVisibility(View.GONE);
+ mTurnOnButton.setText(buttonText);
+ }
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ final boolean isActivated = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ final int autoMode = mUiModeManager.getNightMode();
+ if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+ return mContext.getString(isActivated
+ ? R.string.dark_ui_summary_on_auto_mode_auto
+ : R.string.dark_ui_summary_off_auto_mode_auto);
+ } else {
+ return mContext.getString(isActivated
+ ? R.string.dark_ui_summary_on_auto_mode_never
+ : R.string.dark_ui_summary_off_auto_mode_never);
+ }
+ }
+
+ private final View.OnClickListener mListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final boolean active = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ mUiModeManager.setNightModeActivated(!active);
+ updateNightMode(!active);
+ }
+ };
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ final LayoutPreference preference = screen.findPreference(getPreferenceKey());
+ mTurnOnButton = preference.findViewById(R.id.dark_ui_turn_on_button);
+ mTurnOnButton.setOnClickListener(mListener);
+ mTurnOffButton = preference.findViewById(R.id.dark_ui_turn_off_button);
+ mTurnOffButton.setOnClickListener(mListener);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModeObserver.java b/src/com/android/settings/display/darkmode/DarkModeObserver.java
new file mode 100644
index 0000000..1c076f7
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeObserver.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Observes changes for dark night settings*/
+public class DarkModeObserver {
+ private ContentObserver mContentObserver;
+ private Runnable mCallback;
+ private Context mContext;
+
+ public DarkModeObserver(Context context) {
+ mContext = context;
+ mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ final String setting = uri == null ? null : uri.getLastPathSegment();
+ if (setting != null && mCallback != null) {
+ switch (setting) {
+ case Settings.Secure.UI_NIGHT_MODE:
+ mCallback.run();
+ break;
+ default:
+ }
+ }
+ }
+ };
+ }
+
+ /**
+ * subscribe callback when night mode changed in the database
+ *
+ * @param callback the callback that gets triggered when subscribed
+ */
+ public void subscribe(Runnable callback) {
+ callback.run();
+ mCallback = callback;
+ final Uri uri = Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE);
+ mContext.getContentResolver().registerContentObserver(uri, false, mContentObserver);
+ }
+
+ /**
+ * unsubscribe from dark ui database changes
+ */
+ public void unsubscribe() {
+ mContext.getContentResolver().unregisterContentObserver(mContentObserver);
+ mCallback = null;
+ }
+
+ @VisibleForTesting
+ protected void setContentObserver(ContentObserver co) {
+ mContentObserver = co;
+ }
+
+ @VisibleForTesting
+ protected ContentObserver getContentObserver() {
+ return mContentObserver;
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModePreference.java b/src/com/android/settings/display/darkmode/DarkModePreference.java
new file mode 100644
index 0000000..2a8909f
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModePreference.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import com.android.settings.R;
+import com.android.settings.widget.MasterSwitchPreference;
+
+/**
+ * component for the display settings dark ui summary*/
+public class DarkModePreference extends MasterSwitchPreference {
+
+ private UiModeManager mUiModeManager;
+ private DarkModeObserver mDarkModeObserver;
+ private Runnable mCallback;
+
+ public DarkModePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDarkModeObserver = new DarkModeObserver(context);
+ mUiModeManager = context.getSystemService(UiModeManager.class);
+ mCallback = () -> {
+ updateSummary();
+ };
+ mDarkModeObserver.subscribe(mCallback);
+ }
+
+ @Override
+ public void onAttached() {
+ super.onAttached();
+ mDarkModeObserver.subscribe(mCallback);
+ }
+
+ @Override
+ public void onDetached() {
+ super.onDetached();
+ mDarkModeObserver.unsubscribe();
+ }
+
+ private void updateSummary() {
+ final boolean active = (getContext().getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ final boolean auto = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO;
+
+ String detail;
+ if (active) {
+ detail = getContext().getString(auto
+ ? R.string.dark_ui_summary_on_auto_mode_auto
+ : R.string.dark_ui_summary_on_auto_mode_never);
+ } else {
+ detail = getContext().getString(auto
+ ? R.string.dark_ui_summary_off_auto_mode_auto
+ : R.string.dark_ui_summary_off_auto_mode_never);
+ }
+ String summary = getContext().getString(active
+ ? R.string.dark_ui_summary_on
+ : R.string.dark_ui_summary_off, detail);
+
+ setSummary(summary);
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
new file mode 100644
index 0000000..ab7ea92
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import androidx.preference.DropDownPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+
+/**
+ * Controller for the dark ui option dropdown
+ */
+public class DarkModeScheduleSelectorController extends BasePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private final UiModeManager mUiModeManager;
+ private boolean mPreferenceSet = false;
+ private DropDownPreference mPreference;
+ private String mCurrentMode;
+
+ public DarkModeScheduleSelectorController(Context context, String key) {
+ super(context, key);
+ mUiModeManager = context.getSystemService(UiModeManager.class);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return BasePreferenceController.AVAILABLE;
+ }
+
+ @Override
+ public final void updateState(Preference preference) {
+ mCurrentMode =
+ mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO
+ ? mContext.getString(R.string.dark_ui_auto_mode_auto)
+ : mContext.getString(R.string.dark_ui_auto_mode_never);
+ mPreference.setValue(mCurrentMode);
+ }
+ @Override
+ public final boolean onPreferenceChange(Preference preference, Object newValue) {
+ String newMode = (String) newValue;
+ if (newMode == mCurrentMode) {
+ return false;
+ }
+ mCurrentMode = newMode;
+ if (mCurrentMode == mContext.getString(R.string.dark_ui_auto_mode_never)) {
+ boolean active = (mContext.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_YES) != 0;
+ int mode = active ? UiModeManager.MODE_NIGHT_YES
+ : UiModeManager.MODE_NIGHT_NO;
+ mUiModeManager.setNightMode(mode);
+
+ } else if (mCurrentMode ==
+ mContext.getString(R.string.dark_ui_auto_mode_auto)) {
+ mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO);
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
new file mode 100644
index 0000000..6293316
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.app.settings.SettingsEnums;
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.search.SearchIndexable;
+
+/**
+ * Settings screen for Dark UI Mode
+ */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class DarkModeSettingsFragment extends DashboardFragment {
+
+ private static final String TAG = "DarkModeSettingsFragment";
+ private DarkModeObserver mContentObserver;
+ private Runnable mCallback = () -> {
+ updatePreferenceStates();
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Context context = getContext();
+ mContentObserver = new DarkModeObserver(context);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // Listen for changes only while visible.
+ mContentObserver.subscribe(mCallback);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ // Stop listening for state changes.
+ mContentObserver.unsubscribe();
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.dark_mode_settings;
+ }
+
+ @Override
+ public int getHelpResource() {
+ return R.string.help_url_dark_theme;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.DARK_UI_SETTINGS;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.dark_mode_settings);
+}
diff --git a/src/com/android/settings/fuelgauge/InactiveApps.java b/src/com/android/settings/fuelgauge/InactiveApps.java
index 6c8a954..c386a7d 100644
--- a/src/com/android/settings/fuelgauge/InactiveApps.java
+++ b/src/com/android/settings/fuelgauge/InactiveApps.java
@@ -67,7 +67,8 @@
super.onCreate(icicle);
mUsageStats = getActivity().getSystemService(UsageStatsManager.class);
- addPreferencesFromResource(R.xml.inactive_apps);
+ addPreferencesFromResource(R.xml.dummy_preference_screen);
+ getActivity().setTitle(R.string.inactive_apps_title);
}
@Override
diff --git a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
index 385f8cd..1494293 100644
--- a/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
+++ b/src/com/android/settings/homepage/contextualcards/SettingsContextualCardProvider.java
@@ -78,6 +78,12 @@
.setCardName(CustomSliceRegistry.FACE_ENROLL_SLICE_URI.toString())
.setCardCategory(ContextualCard.Category.DEFAULT)
.build();
+ final ContextualCard darkThemeCard =
+ ContextualCard.newBuilder()
+ .setSliceUri(CustomSliceRegistry.DARK_THEME_SLICE_URI.toString())
+ .setCardName(CustomSliceRegistry.DARK_THEME_SLICE_URI.toString())
+ .setCardCategory(ContextualCard.Category.IMPORTANT)
+ .build();
final ContextualCardList cards = ContextualCardList.newBuilder()
.addCard(wifiCard)
.addCard(connectedDeviceCard)
@@ -86,6 +92,7 @@
.addCard(notificationChannelCard)
.addCard(contextualAdaptiveSleepCard)
.addCard(contextualFaceSettingsCard)
+ .addCard(darkThemeCard)
.build();
return cards;
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
index d1051fe..3cd4ca3 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
@@ -201,7 +201,8 @@
}
@WorkerThread
- private static List<BatteryTip> refreshBatteryTips(Context context) {
+ @VisibleForTesting
+ static List<BatteryTip> refreshBatteryTips(Context context) {
final BatteryStatsHelperLoader statsLoader = new BatteryStatsHelperLoader(context);
final BatteryStatsHelper statsHelper = statsLoader.loadInBackground();
final BatteryTipLoader loader = new BatteryTipLoader(context, statsHelper);
diff --git a/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java
new file mode 100644
index 0000000..36a3980
--- /dev/null
+++ b/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSlice.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.homepage.contextualcards.slices;
+
+import static androidx.slice.builders.ListBuilder.ICON_IMAGE;
+
+import android.annotation.ColorInt;
+import android.app.PendingIntent;
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
+import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import androidx.slice.builders.SliceAction;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.slices.CustomSliceRegistry;
+import com.android.settings.slices.CustomSliceable;
+
+public class DarkThemeSlice implements CustomSliceable {
+ private static final String TAG = "DarkThemeSlice";
+ private static final int BATTERY_LEVEL_THRESHOLD = 50;
+ private static final int DELAY_TIME_EXECUTING_DARK_THEME = 200;
+
+ // Keep the slice even Dark theme mode changed when it is on HomePage
+ @VisibleForTesting
+ static boolean sKeepSliceShow;
+ @VisibleForTesting
+ static long sActiveUiSession = -1000;
+
+ private final Context mContext;
+ private final UiModeManager mUiModeManager;
+
+ public DarkThemeSlice(Context context) {
+ mContext = context;
+ mUiModeManager = context.getSystemService(UiModeManager.class);
+ }
+
+ @Override
+ public Slice getSlice() {
+ final long currentUiSession = FeatureFactory.getFactory(mContext)
+ .getSlicesFeatureProvider().getUiSessionToken();
+ if (currentUiSession != sActiveUiSession) {
+ sActiveUiSession = currentUiSession;
+ sKeepSliceShow = false;
+ }
+ if (!sKeepSliceShow && !isAvailable(mContext)) {
+ return null;
+ }
+ sKeepSliceShow = true;
+ final PendingIntent toggleAction = getBroadcastIntent(mContext);
+ @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext);
+ final IconCompat icon =
+ IconCompat.createWithResource(mContext, R.drawable.dark_theme);
+ final boolean isChecked = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES;
+ return new ListBuilder(mContext, CustomSliceRegistry.DARK_THEME_SLICE_URI,
+ ListBuilder.INFINITY)
+ .setAccentColor(color)
+ .addRow(new ListBuilder.RowBuilder()
+ .setTitle(mContext.getText(R.string.dark_theme_slice_title))
+ .setTitleItem(icon, ICON_IMAGE)
+ .setSubtitle(mContext.getText(R.string.dark_theme_slice_subtitle))
+ .setPrimaryAction(
+ SliceAction.createToggle(toggleAction, null /* actionTitle */,
+ isChecked)))
+ .build();
+ }
+
+ @Override
+ public Uri getUri() {
+ return CustomSliceRegistry.DARK_THEME_SLICE_URI;
+ }
+
+ @Override
+ public void onNotifyChange(Intent intent) {
+ final boolean isChecked = intent.getBooleanExtra(android.app.slice.Slice.EXTRA_TOGGLE_STATE,
+ false);
+ // make toggle transition more smooth before dark theme takes effect
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ mUiModeManager.setNightMode(
+ isChecked ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO);
+ }, DELAY_TIME_EXECUTING_DARK_THEME);
+ }
+
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @VisibleForTesting
+ boolean isAvailable(Context context) {
+ // checking dark theme mode.
+ if (mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_YES) {
+ return false;
+ }
+
+ // checking the current battery level
+ final BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ final int level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
+ Log.d(TAG, "battery level=" + level);
+
+ return level <= BATTERY_LEVEL_THRESHOLD;
+ }
+}
diff --git a/src/com/android/settings/network/ActiveSubsciptionsListener.java b/src/com/android/settings/network/ActiveSubsciptionsListener.java
index e58de5c..cb3e061 100644
--- a/src/com/android/settings/network/ActiveSubsciptionsListener.java
+++ b/src/com/android/settings/network/ActiveSubsciptionsListener.java
@@ -24,6 +24,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
@@ -37,6 +38,8 @@
public abstract class ActiveSubsciptionsListener
extends SubscriptionManager.OnSubscriptionsChangedListener {
+ private static final String TAG = "ActiveSubsciptions";
+
/**
* Constructor
*
@@ -155,6 +158,18 @@
}
mIsCachedDataAvailable = mIsMonitoringDataChange;
mCachedActiveSubscriptionInfo = getSubscriptionManager().getActiveSubscriptionInfoList();
+
+ if ((mCachedActiveSubscriptionInfo == null)
+ || (mCachedActiveSubscriptionInfo.size() <= 0)) {
+ Log.d(TAG, "active subscriptions: " + mCachedActiveSubscriptionInfo);
+ } else {
+ final StringBuilder logString = new StringBuilder("active subscriptions:");
+ for (SubscriptionInfo subInfo : mCachedActiveSubscriptionInfo) {
+ logString.append(" " + subInfo.getSubscriptionId());
+ }
+ Log.d(TAG, logString.toString());
+ }
+
return mCachedActiveSubscriptionInfo;
}
@@ -246,7 +261,10 @@
}
private boolean clearCachedSubId(int subId) {
- if ((!mIsCachedDataAvailable) || (mCachedActiveSubscriptionInfo == null)) {
+ if (!mIsCachedDataAvailable) {
+ return false;
+ }
+ if (mCachedActiveSubscriptionInfo == null) {
return false;
}
for (SubscriptionInfo subInfo : mCachedActiveSubscriptionInfo) {
diff --git a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
index 0086fec..bdadfe4 100644
--- a/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
+++ b/src/com/android/settings/network/PrivateDnsModeDialogPreference.java
@@ -40,6 +40,7 @@
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
@@ -176,6 +177,15 @@
mRadioGroup.setOnCheckedChangeListener(this);
mRadioGroup.check(PRIVATE_DNS_MAP.getOrDefault(mMode, R.id.private_dns_mode_opportunistic));
+ // Initial radio button text
+ final RadioButton offRadioButton = view.findViewById(R.id.private_dns_mode_off);
+ offRadioButton.setText(R.string.private_dns_mode_off);
+ final RadioButton opportunisticRadioButton =
+ view.findViewById(R.id.private_dns_mode_opportunistic);
+ opportunisticRadioButton.setText(R.string.private_dns_mode_opportunistic);
+ final RadioButton providerRadioButton = view.findViewById(R.id.private_dns_mode_provider);
+ providerRadioButton.setText(R.string.private_dns_mode_provider);
+
final TextView helpTextView = view.findViewById(R.id.private_dns_help_info);
helpTextView.setMovementMethod(LinkMovementMethod.getInstance());
final Intent helpIntent = HelpUtils.getHelpIntent(context,
diff --git a/src/com/android/settings/network/ProxySubscriptionManager.java b/src/com/android/settings/network/ProxySubscriptionManager.java
index 85ee4b2..18b4ac9 100644
--- a/src/com/android/settings/network/ProxySubscriptionManager.java
+++ b/src/com/android/settings/network/ProxySubscriptionManager.java
@@ -35,8 +35,7 @@
/**
* A proxy to the subscription manager
*/
-public class ProxySubscriptionManager extends SubscriptionManager.OnSubscriptionsChangedListener
- implements LifecycleObserver {
+public class ProxySubscriptionManager implements LifecycleObserver {
/**
* Interface for monitor active subscriptions list changing
@@ -65,7 +64,7 @@
if (sSingleton != null) {
return sSingleton;
}
- sSingleton = new ProxySubscriptionManager(context);
+ sSingleton = new ProxySubscriptionManager(context.getApplicationContext());
return sSingleton;
}
@@ -110,12 +109,6 @@
}
}
- @Override
- public void onSubscriptionsChanged() {
- clearCache();
- notifyAllListeners();
- }
-
/**
* Lifecycle for data within proxy
*
diff --git a/src/com/android/settings/network/telephony/MobileNetworkActivity.java b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
index 8e3aac8..9eb9e71 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkActivity.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkActivity.java
@@ -89,9 +89,12 @@
mProxySubscriptionMgr.setLifecycle(getLifecycle());
mProxySubscriptionMgr.addActiveSubscriptionsListener(this);
+ final Intent startIntent = getIntent();
mCurSubscriptionId = savedInstanceState != null
? savedInstanceState.getInt(Settings.EXTRA_SUB_ID, SUB_ID_NULL)
- : SUB_ID_NULL;
+ : ((startIntent != null)
+ ? startIntent.getIntExtra(Settings.EXTRA_SUB_ID, SUB_ID_NULL)
+ : SUB_ID_NULL);
final SubscriptionInfo subscription = getSubscription();
updateTitleAndNavigation(subscription);
diff --git a/src/com/android/settings/notification/SoundSettings.java b/src/com/android/settings/notification/SoundSettings.java
index 6e33711..7d14691 100644
--- a/src/com/android/settings/notification/SoundSettings.java
+++ b/src/com/android/settings/notification/SoundSettings.java
@@ -25,7 +25,6 @@
import android.os.Message;
import android.os.UserHandle;
import android.preference.SeekBarVolumizer;
-import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
@@ -39,11 +38,11 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.sound.HandsFreeProfileOutputPreferenceController;
import com.android.settings.widget.PreferenceCategoryController;
-import com.android.settings.widget.UpdatableListPreferenceDialogFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.Instrumentable;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
+import com.android.settingslib.widget.UpdatableListPreferenceDialogFragment;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/src/com/android/settings/panel/WifiPanel.java b/src/com/android/settings/panel/WifiPanel.java
index 36ee117..0efa804 100644
--- a/src/com/android/settings/panel/WifiPanel.java
+++ b/src/com/android/settings/panel/WifiPanel.java
@@ -20,12 +20,14 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.util.FeatureFlagUtils;
import com.android.settings.R;
import com.android.settings.SubSettings;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.WifiSettings2;
import java.util.ArrayList;
import java.util.List;
@@ -61,11 +63,20 @@
public Intent getSeeMoreIntent() {
final String screenTitle =
mContext.getText(R.string.wifi_settings).toString();
- final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
- WifiSettings.class.getName(),
- null /* key */,
- screenTitle,
- SettingsEnums.WIFI);
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings2.class.getName(),
+ null /* key */,
+ screenTitle,
+ SettingsEnums.WIFI);
+ } else {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings.class.getName(),
+ null /* key */,
+ screenTitle,
+ SettingsEnums.WIFI);
+ }
intent.setClassName(mContext.getPackageName(), SubSettings.class.getName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index b81b0c4..b5670ca 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -30,7 +30,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
@@ -57,8 +56,6 @@
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupdesign.GlifLayout;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -608,8 +605,10 @@
public void handleLeftButton() {
if (mUiStage.leftMode == LeftButtonMode.Retry) {
- mChosenPattern.zeroize();
- mChosenPattern = null;
+ if (mChosenPattern != null) {
+ mChosenPattern.zeroize();
+ mChosenPattern = null;
+ }
mLockPatternView.clearPattern();
updateStage(Stage.Introduction);
} else {
diff --git a/src/com/android/settings/search/BaseSearchIndexProvider.java b/src/com/android/settings/search/BaseSearchIndexProvider.java
index abf1c99..da89062 100644
--- a/src/com/android/settings/search/BaseSearchIndexProvider.java
+++ b/src/com/android/settings/search/BaseSearchIndexProvider.java
@@ -77,8 +77,25 @@
}
@Override
+ @CallSuper
public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context, boolean enabled) {
- return null;
+ final List<SearchIndexableRaw> dynamicRaws = new ArrayList<>();
+ final List<AbstractPreferenceController> controllers = getPreferenceControllers(context);
+ if (controllers == null || controllers.isEmpty()) {
+ return dynamicRaws;
+ }
+ for (AbstractPreferenceController controller : controllers) {
+ if (controller instanceof PreferenceControllerMixin) {
+ ((PreferenceControllerMixin) controller).updateDynamicRawDataToIndex(dynamicRaws);
+ } else if (controller instanceof BasePreferenceController) {
+ ((BasePreferenceController) controller).updateDynamicRawDataToIndex(dynamicRaws);
+ } else {
+ Log.e(TAG, controller.getClass().getName()
+ + " must implement " + PreferenceControllerMixin.class.getName()
+ + " treating the dynamic indexable");
+ }
+ }
+ return dynamicRaws;
}
@Override
diff --git a/src/com/android/settings/search/actionbar/SearchMenuController.java b/src/com/android/settings/search/actionbar/SearchMenuController.java
index 0243c09..9e22bbf 100644
--- a/src/com/android/settings/search/actionbar/SearchMenuController.java
+++ b/src/com/android/settings/search/actionbar/SearchMenuController.java
@@ -42,6 +42,7 @@
public class SearchMenuController implements LifecycleObserver, OnCreateOptionsMenu {
public static final String NEED_SEARCH_ICON_IN_ACTION_BAR = "need_search_icon_in_action_bar";
+ public static final int MENU_SEARCH = Menu.FIRST + 10;
private final Fragment mHost;
private final int mPageId;
@@ -80,7 +81,11 @@
if (arguments != null && !arguments.getBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, true)) {
return;
}
- final MenuItem searchItem = menu.add(Menu.NONE, Menu.NONE, 0 /* order */,
+ // menu contains search item, skip it
+ if (menu.findItem(MENU_SEARCH) != null) {
+ return;
+ }
+ final MenuItem searchItem = menu.add(Menu.NONE, MENU_SEARCH, 0 /* order */,
R.string.search_menu);
searchItem.setIcon(R.drawable.ic_search_24dp);
searchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
diff --git a/src/com/android/settings/security/screenlock/LockScreenPreferenceController.java b/src/com/android/settings/security/screenlock/LockScreenPreferenceController.java
index 7d83f40..8cf0825 100644
--- a/src/com/android/settings/security/screenlock/LockScreenPreferenceController.java
+++ b/src/com/android/settings/security/screenlock/LockScreenPreferenceController.java
@@ -16,8 +16,6 @@
package com.android.settings.security.screenlock;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-
import android.content.Context;
import android.os.UserHandle;
@@ -52,14 +50,7 @@
@Override
public int getAvailabilityStatus() {
- if (!mLockPatternUtils.isSecure(MY_USER_ID)) {
- return mLockPatternUtils.isLockScreenDisabled(MY_USER_ID)
- ? DISABLED_FOR_USER : AVAILABLE_UNSEARCHABLE;
- } else {
- return mLockPatternUtils.getKeyguardStoredPasswordQuality(MY_USER_ID)
- == PASSWORD_QUALITY_UNSPECIFIED
- ? DISABLED_FOR_USER : AVAILABLE_UNSEARCHABLE;
- }
+ return AVAILABLE_UNSEARCHABLE;
}
@Override
diff --git a/src/com/android/settings/slices/CustomSliceRegistry.java b/src/com/android/settings/slices/CustomSliceRegistry.java
index d5b631a..a019687 100644
--- a/src/com/android/settings/slices/CustomSliceRegistry.java
+++ b/src/com/android/settings/slices/CustomSliceRegistry.java
@@ -37,6 +37,7 @@
import com.android.settings.homepage.contextualcards.slices.BluetoothDevicesSlice;
import com.android.settings.homepage.contextualcards.slices.ContextualAdaptiveSleepSlice;
import com.android.settings.homepage.contextualcards.slices.ContextualNotificationChannelSlice;
+import com.android.settings.homepage.contextualcards.slices.DarkThemeSlice;
import com.android.settings.homepage.contextualcards.slices.FaceSetupSlice;
import com.android.settings.homepage.contextualcards.slices.LowStorageSlice;
import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice;
@@ -332,6 +333,16 @@
.appendPath("media_output_indicator")
.build();
+ /**
+ * Backing Uri for the Dark theme Slice.
+ */
+ public static final Uri DARK_THEME_SLICE_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SettingsSliceProvider.SLICE_AUTHORITY)
+ .appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
+ .appendPath("dark_theme")
+ .build();
+
@VisibleForTesting
static final Map<Uri, Class<? extends CustomSliceable>> sUriToSlice;
@@ -357,6 +368,7 @@
sUriToSlice.put(NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class);
sUriToSlice.put(STORAGE_SLICE_URI, StorageSlice.class);
sUriToSlice.put(WIFI_SLICE_URI, WifiSlice.class);
+ sUriToSlice.put(DARK_THEME_SLICE_URI, DarkThemeSlice.class);
}
public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {
diff --git a/src/com/android/settings/slices/SliceData.java b/src/com/android/settings/slices/SliceData.java
index 0f3a678..60f5e3f 100644
--- a/src/com/android/settings/slices/SliceData.java
+++ b/src/com/android/settings/slices/SliceData.java
@@ -73,6 +73,8 @@
private final String mUnavailableSliceSubtitle;
+ private final boolean mIsPublicSlice;
+
public String getKey() {
return mKey;
}
@@ -117,6 +119,10 @@
return mUnavailableSliceSubtitle;
}
+ public boolean isPublicSlice() {
+ return mIsPublicSlice;
+ }
+
private SliceData(Builder builder) {
mKey = builder.mKey;
mTitle = builder.mTitle;
@@ -129,6 +135,7 @@
mPreferenceController = builder.mPrefControllerClassName;
mSliceType = builder.mSliceType;
mUnavailableSliceSubtitle = builder.mUnavailableSliceSubtitle;
+ mIsPublicSlice = builder.mIsPublicSlice;
}
@Override
@@ -168,6 +175,8 @@
private String mUnavailableSliceSubtitle;
+ private boolean mIsPublicSlice;
+
public Builder setKey(String key) {
mKey = key;
return this;
@@ -224,6 +233,11 @@
return this;
}
+ public Builder setIsPublicSlice(boolean isPublicSlice) {
+ mIsPublicSlice = isPublicSlice;
+ return this;
+ }
+
public SliceData build() {
if (TextUtils.isEmpty(mKey)) {
throw new InvalidSliceDataException("Key cannot be empty");
diff --git a/src/com/android/settings/slices/SliceDataConverter.java b/src/com/android/settings/slices/SliceDataConverter.java
index 551a186..5608169 100644
--- a/src/com/android/settings/slices/SliceDataConverter.java
+++ b/src/com/android/settings/slices/SliceDataConverter.java
@@ -212,6 +212,7 @@
final int sliceType = controller.getSliceType();
final String unavailableSliceSubtitle = bundle.getString(
METADATA_UNAVAILABLE_SLICE_SUBTITLE);
+ final boolean isPublicSlice = controller.isPublicSlice();
final SliceData xmlSlice = new SliceData.Builder()
.setKey(key)
@@ -224,6 +225,7 @@
.setFragmentName(fragmentName)
.setSliceType(sliceType)
.setUnavailableSliceSubtitle(unavailableSliceSubtitle)
+ .setIsPublicSlice(isPublicSlice)
.build();
xmlSliceData.add(xmlSlice);
diff --git a/src/com/android/settings/slices/Sliceable.java b/src/com/android/settings/slices/Sliceable.java
index aab4906..ad27b7c 100644
--- a/src/com/android/settings/slices/Sliceable.java
+++ b/src/com/android/settings/slices/Sliceable.java
@@ -25,6 +25,8 @@
import android.net.Uri;
import android.widget.Toast;
+import androidx.slice.Slice;
+
import com.android.settings.R;
/**
@@ -49,13 +51,24 @@
* <p>
* This does not guarantee the setting is available.
*
- * @return {@code true} if the controller should be used externally as a Slice.
+ * @return {@code true} if the controller should be used as a Slice.
*/
default boolean isSliceable() {
return false;
}
/**
+ * Determines if the {@link Slice} should be public to other apps.
+ * This does not guarantee the setting is available.
+ *
+ * @return {@code true} if the controller should be used as a Slice, and is
+ * publicly visible to other apps.
+ */
+ default boolean isPublicSlice() {
+ return false;
+ }
+
+ /**
* Returns uri for this slice (if it's a slice).
*/
default Uri getSliceUri() {
diff --git a/src/com/android/settings/slices/SlicesDatabaseHelper.java b/src/com/android/settings/slices/SlicesDatabaseHelper.java
index 6c13ef9..fe4420b 100644
--- a/src/com/android/settings/slices/SlicesDatabaseHelper.java
+++ b/src/com/android/settings/slices/SlicesDatabaseHelper.java
@@ -36,7 +36,7 @@
private static final String DATABASE_NAME = "slices_index.db";
private static final String SHARED_PREFS_TAG = "slices_shared_prefs";
- private static final int DATABASE_VERSION = 7;
+ private static final int DATABASE_VERSION = 8;
public interface Tables {
String TABLE_SLICES_INDEX = "slices_index";
@@ -98,6 +98,11 @@
* The uri of slice.
*/
String SLICE_URI = "slice_uri";
+
+ /**
+ * Whether the slice should be exposed publicly.
+ */
+ String PUBLIC_SLICE = "public_slice";
}
private static final String CREATE_SLICES_TABLE =
@@ -124,6 +129,12 @@
IndexColumns.SLICE_TYPE +
", " +
IndexColumns.UNAVAILABLE_SLICE_SUBTITLE +
+ ", "
+ +
+ IndexColumns.PUBLIC_SLICE
+ +
+ " INTEGER DEFAULT 0 "
+ +
");";
private final Context mContext;
diff --git a/src/com/android/settings/slices/SlicesIndexer.java b/src/com/android/settings/slices/SlicesIndexer.java
index 7b49b4b..3a68a32 100644
--- a/src/com/android/settings/slices/SlicesIndexer.java
+++ b/src/com/android/settings/slices/SlicesIndexer.java
@@ -112,6 +112,7 @@
values.put(IndexColumns.SLICE_TYPE, dataRow.getSliceType());
values.put(IndexColumns.UNAVAILABLE_SLICE_SUBTITLE,
dataRow.getUnavailableSliceSubtitle());
+ values.put(IndexColumns.PUBLIC_SLICE, dataRow.isPublicSlice());
database.replaceOrThrow(Tables.TABLE_SLICES_INDEX, null /* nullColumnHack */,
values);
diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java
index 3193afb..0f4fbc3 100644
--- a/src/com/android/settings/widget/RadioButtonPickerFragment.java
+++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java
@@ -150,12 +150,10 @@
/**
* A chance for subclasses to bind additional things to the preference.
*/
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
public void bindPreferenceExtra(RadioButtonPreference pref,
String key, CandidateInfo info, String defaultKey, String systemDefaultKey) {
}
- @VisibleForTesting
public void updateCandidates() {
mCandidates.clear();
final List<? extends CandidateInfo> candidateList = getCandidates();
@@ -204,7 +202,6 @@
}
}
- @VisibleForTesting
public RadioButtonPreference bindPreference(RadioButtonPreference pref,
String key, CandidateInfo info, String defaultKey) {
pref.setTitle(info.loadLabel());
@@ -218,7 +215,6 @@
return pref;
}
- @VisibleForTesting
public void updateCheckedState(String selectedKey) {
final PreferenceScreen screen = getPreferenceScreen();
if (screen != null) {
@@ -236,7 +232,6 @@
}
}
- @VisibleForTesting
public void mayCheckOnlyRadioButton() {
final PreferenceScreen screen = getPreferenceScreen();
// If there is only 1 thing on screen, select it.
diff --git a/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java b/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java
deleted file mode 100644
index bd7f78a..0000000
--- a/src/com/android/settings/widget/UpdatableListPreferenceDialogFragment.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.widget;
-
-import android.content.res.TypedArray;
-import android.os.Bundle;
-import android.widget.ArrayAdapter;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog.Builder;
-import androidx.preference.ListPreference;
-import androidx.preference.PreferenceDialogFragmentCompat;
-
-import com.android.settingslib.core.instrumentation.Instrumentable;
-
-import java.util.ArrayList;
-
-/**
- * {@link PreferenceDialogFragmentCompat} that updates the available options
- * when {@code onListPreferenceUpdated} is called."
- */
-public class UpdatableListPreferenceDialogFragment extends PreferenceDialogFragmentCompat implements
- Instrumentable {
-
- private static final String SAVE_STATE_INDEX = "UpdatableListPreferenceDialogFragment.index";
- private static final String SAVE_STATE_ENTRIES =
- "UpdatableListPreferenceDialogFragment.entries";
- private static final String SAVE_STATE_ENTRY_VALUES =
- "UpdatableListPreferenceDialogFragment.entryValues";
- private static final String METRICS_CATEGORY_KEY = "metrics_category_key";
- private ArrayAdapter mAdapter;
- private int mClickedDialogEntryIndex;
- private ArrayList<CharSequence> mEntries;
- private CharSequence[] mEntryValues;
- private int mMetricsCategory = Instrumentable.METRICS_CATEGORY_UNKNOWN;
-
- public static UpdatableListPreferenceDialogFragment newInstance(
- String key, int metricsCategory) {
- UpdatableListPreferenceDialogFragment fragment =
- new UpdatableListPreferenceDialogFragment();
- Bundle args = new Bundle(1);
- args.putString(ARG_KEY, key);
- args.putInt(METRICS_CATEGORY_KEY, metricsCategory);
- fragment.setArguments(args);
- return fragment;
- }
-
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final Bundle bundle = getArguments();
- mMetricsCategory =
- bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
- if (savedInstanceState == null) {
- mEntries = new ArrayList<>();
- setPreferenceData(getListPreference());
- } else {
- mClickedDialogEntryIndex = savedInstanceState.getInt(SAVE_STATE_INDEX, 0);
- mEntries = savedInstanceState.getCharSequenceArrayList(SAVE_STATE_ENTRIES);
- mEntryValues =
- savedInstanceState.getCharSequenceArray(SAVE_STATE_ENTRY_VALUES);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putInt(SAVE_STATE_INDEX, mClickedDialogEntryIndex);
- outState.putCharSequenceArrayList(SAVE_STATE_ENTRIES, mEntries);
- outState.putCharSequenceArray(SAVE_STATE_ENTRY_VALUES, mEntryValues);
- }
-
- @Override
- public void onDialogClosed(boolean positiveResult) {
- if (positiveResult && mClickedDialogEntryIndex >= 0) {
- final ListPreference preference = getListPreference();
- final String value = mEntryValues[mClickedDialogEntryIndex].toString();
- if (preference.callChangeListener(value)) {
- preference.setValue(value);
- }
- }
- }
-
- @VisibleForTesting
- void setAdapter(ArrayAdapter adapter) {
- mAdapter = adapter;
- }
-
- @VisibleForTesting
- void setEntries(ArrayList<CharSequence> entries) {
- mEntries = entries;
- }
-
- @VisibleForTesting
- ArrayAdapter getAdapter() {
- return mAdapter;
- }
-
- @VisibleForTesting
- void setMetricsCategory(Bundle bundle) {
- mMetricsCategory =
- bundle.getInt(METRICS_CATEGORY_KEY, Instrumentable.METRICS_CATEGORY_UNKNOWN);
- }
-
- @Override
- protected void onPrepareDialogBuilder(Builder builder) {
- super.onPrepareDialogBuilder(builder);
- final TypedArray a = getContext().obtainStyledAttributes(
- null,
- com.android.internal.R.styleable.AlertDialog,
- com.android.internal.R.attr.alertDialogStyle, 0);
-
- mAdapter = new ArrayAdapter<>(
- getContext(),
- a.getResourceId(
- com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
- com.android.internal.R.layout.select_dialog_singlechoice),
- mEntries);
-
- builder.setSingleChoiceItems(mAdapter, mClickedDialogEntryIndex,
- (dialog, which) -> {
- mClickedDialogEntryIndex = which;
- onClick(dialog, -1);
- dialog.dismiss();
- });
- builder.setPositiveButton(null, null);
- a.recycle();
- }
-
- @Override
- public int getMetricsCategory() {
- return mMetricsCategory;
- }
-
- @VisibleForTesting
- ListPreference getListPreference() {
- return (ListPreference) getPreference();
- }
-
- private void setPreferenceData(ListPreference preference) {
- mEntries.clear();
- mClickedDialogEntryIndex = preference.findIndexOfValue(preference.getValue());
- for (CharSequence entry : preference.getEntries()) {
- mEntries.add(entry);
- }
- mEntryValues = preference.getEntryValues();
- }
-
- public void onListPreferenceUpdated(ListPreference preference) {
- if (mAdapter != null) {
- setPreferenceData(preference);
- mAdapter.notifyDataSetChanged();
- }
- }
-}
diff --git a/src/com/android/settings/wifi/NetworkRequestDialogActivity.java b/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
index 1f2b221..2326a0e 100644
--- a/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
+++ b/src/com/android/settings/wifi/NetworkRequestDialogActivity.java
@@ -26,12 +26,16 @@
import android.net.wifi.WifiManager.NetworkRequestUserSelectionCallback;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Message;
import android.widget.Toast;
+
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
+
import com.android.settings.R;
import com.android.settings.wifi.NetworkRequestErrorDialogFragment.ERROR_DIALOG_TYPE;
+
import java.util.List;
/**
@@ -115,7 +119,7 @@
final WifiManager wifiManager = getSystemService(WifiManager.class);
if (wifiManager != null) {
- wifiManager.registerNetworkRequestMatchCallback(this, mHandler);
+ wifiManager.registerNetworkRequestMatchCallback(new HandlerExecutor(mHandler), this);
}
// Sets time-out to stop scanning.
mHandler.sendEmptyMessageDelayed(MESSAGE_STOP_SCAN_WIFI_LIST, DELAY_TIME_STOP_SCAN_MS);
diff --git a/src/com/android/settings/wifi/RequestToggleWiFiActivity.java b/src/com/android/settings/wifi/RequestToggleWiFiActivity.java
index 879a93e..034ec56 100644
--- a/src/com/android/settings/wifi/RequestToggleWiFiActivity.java
+++ b/src/com/android/settings/wifi/RequestToggleWiFiActivity.java
@@ -29,7 +29,6 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.Toast;
import androidx.annotation.NonNull;
@@ -313,11 +312,6 @@
finish();
}
} break;
-
- case WifiManager.ERROR: {
- Toast.makeText(activity, R.string.wifi_error, Toast.LENGTH_SHORT).show();
- finish();
- } break;
}
}
}
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 0ba2a94..9fb7546 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -44,6 +44,7 @@
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
@@ -70,6 +71,7 @@
import com.android.settings.ProxySelector;
import com.android.settings.R;
import com.android.settings.wifi.details.WifiPrivacyPreferenceController;
+import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
@@ -119,18 +121,22 @@
public static final int WIFI_EAP_METHOD_AKA_PRIME = 6;
/* These values come from "wifi_peap_phase2_entries" resource array */
- public static final int WIFI_PEAP_PHASE2_NONE = 0;
- public static final int WIFI_PEAP_PHASE2_MSCHAPV2 = 1;
- public static final int WIFI_PEAP_PHASE2_GTC = 2;
- public static final int WIFI_PEAP_PHASE2_SIM = 3;
- public static final int WIFI_PEAP_PHASE2_AKA = 4;
- public static final int WIFI_PEAP_PHASE2_AKA_PRIME = 5;
+ public static final int WIFI_PEAP_PHASE2_MSCHAPV2 = 0;
+ public static final int WIFI_PEAP_PHASE2_GTC = 1;
+ public static final int WIFI_PEAP_PHASE2_SIM = 2;
+ public static final int WIFI_PEAP_PHASE2_AKA = 3;
+ public static final int WIFI_PEAP_PHASE2_AKA_PRIME = 4;
+ /* These values come from "wifi_ttls_phase2_entries" resource array */
+ public static final int WIFI_TTLS_PHASE2_PAP = 0;
+ public static final int WIFI_TTLS_PHASE2_MSCHAP = 1;
+ public static final int WIFI_TTLS_PHASE2_MSCHAPV2 = 2;
+ public static final int WIFI_TTLS_PHASE2_GTC = 3;
/* Phase2 methods supported by PEAP are limited */
private ArrayAdapter<CharSequence> mPhase2PeapAdapter;
- /* Full list of phase2 methods */
- private ArrayAdapter<CharSequence> mPhase2FullAdapter;
+ /* Phase2 methods supported by TTLS are limited */
+ private ArrayAdapter<CharSequence> mPhase2TtlsAdapter;
// e.g. AccessPoint.SECURITY_NONE
@VisibleForTesting
@@ -152,7 +158,7 @@
private Spinner mEapOcspSpinner;
private TextView mEapDomainView;
private Spinner mPhase2Spinner;
- // Associated with mPhase2Spinner, one of mPhase2FullAdapter or mPhase2PeapAdapter
+ // Associated with mPhase2Spinner, one of mPhase2TtlsAdapter or mPhase2PeapAdapter
private ArrayAdapter<CharSequence> mPhase2Adapter;
private Spinner mEapUserCertSpinner;
private TextView mEapIdentityView;
@@ -241,7 +247,7 @@
R.array.wifi_peap_phase2_entries_with_sim_auth);
}
- mPhase2FullAdapter = getSpinnerAdapter(R.array.wifi_phase2_entries);
+ mPhase2TtlsAdapter = getSpinnerAdapter(R.array.wifi_ttls_phase2_entries);
mUnspecifiedCertString = mContext.getString(R.string.wifi_unspecified);
mMultipleCertSetString = mContext.getString(R.string.wifi_multiple_cert_added);
@@ -289,9 +295,14 @@
? HIDDEN_NETWORK
: NOT_HIDDEN_NETWORK);
- final int prefMacValue =
- WifiPrivacyPreferenceController.translateMacRandomizedValueToPrefValue(
- config.macRandomizationSetting);
+ int prefMacValue;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ prefMacValue = WifiPrivacyPreferenceController2
+ .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting);
+ } else {
+ prefMacValue = WifiPrivacyPreferenceController
+ .translateMacRandomizedValueToPrefValue(config.macRandomizationSetting);
+ }
mPrivacySettingsSpinner.setSelection(prefMacValue);
if (config.getIpAssignment() == IpAssignment.STATIC) {
@@ -706,9 +717,6 @@
// Map the index from the mPhase2PeapAdapter to the one used
// by the API which has the full list of PEAP methods.
switch(phase2Method) {
- case WIFI_PEAP_PHASE2_NONE:
- config.enterpriseConfig.setPhase2Method(Phase2.NONE);
- break;
case WIFI_PEAP_PHASE2_MSCHAPV2:
config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
break;
@@ -729,9 +737,27 @@
break;
}
break;
+ case Eap.TTLS:
+ // The default index from mPhase2TtlsAdapter maps to the API
+ switch(phase2Method) {
+ case WIFI_TTLS_PHASE2_PAP:
+ config.enterpriseConfig.setPhase2Method(Phase2.PAP);
+ break;
+ case WIFI_TTLS_PHASE2_MSCHAP:
+ config.enterpriseConfig.setPhase2Method(Phase2.MSCHAP);
+ break;
+ case WIFI_TTLS_PHASE2_MSCHAPV2:
+ config.enterpriseConfig.setPhase2Method(Phase2.MSCHAPV2);
+ break;
+ case WIFI_TTLS_PHASE2_GTC:
+ config.enterpriseConfig.setPhase2Method(Phase2.GTC);
+ break;
+ default:
+ Log.e(TAG, "Unknown phase2 method" + phase2Method);
+ break;
+ }
+ break;
default:
- // The default index from mPhase2FullAdapter maps to the API
- config.enterpriseConfig.setPhase2Method(phase2Method);
break;
}
@@ -843,9 +869,14 @@
}
if (mPrivacySettingsSpinner != null) {
- final int macValue =
- WifiPrivacyPreferenceController.translatePrefValueToMacRandomizedValue(
- mPrivacySettingsSpinner.getSelectedItemPosition());
+ int macValue;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ macValue = WifiPrivacyPreferenceController2.translatePrefValueToMacRandomizedValue(
+ mPrivacySettingsSpinner.getSelectedItemPosition());
+ } else {
+ macValue = WifiPrivacyPreferenceController.translatePrefValueToMacRandomizedValue(
+ mPrivacySettingsSpinner.getSelectedItemPosition());
+ }
config.macRandomizationSetting = macValue;
}
@@ -1105,9 +1136,6 @@
switch (eapMethod) {
case Eap.PEAP:
switch (phase2Method) {
- case Phase2.NONE:
- mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_NONE);
- break;
case Phase2.MSCHAPV2:
mPhase2Spinner.setSelection(WIFI_PEAP_PHASE2_MSCHAPV2);
break;
@@ -1128,8 +1156,26 @@
break;
}
break;
+ case Eap.TTLS:
+ switch (phase2Method) {
+ case Phase2.PAP:
+ mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_PAP);
+ break;
+ case Phase2.MSCHAP:
+ mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAP);
+ break;
+ case Phase2.MSCHAPV2:
+ mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_MSCHAPV2);
+ break;
+ case Phase2.GTC:
+ mPhase2Spinner.setSelection(WIFI_TTLS_PHASE2_GTC);
+ break;
+ default:
+ Log.e(TAG, "Invalid phase 2 method " + phase2Method);
+ break;
+ }
+ break;
default:
- mPhase2Spinner.setSelection(phase2Method);
break;
}
if (!TextUtils.isEmpty(enterpriseConfig.getCaPath())) {
@@ -1230,8 +1276,8 @@
break;
case WIFI_EAP_METHOD_TTLS:
// Reset adapter if needed
- if (mPhase2Adapter != mPhase2FullAdapter) {
- mPhase2Adapter = mPhase2FullAdapter;
+ if (mPhase2Adapter != mPhase2TtlsAdapter) {
+ mPhase2Adapter = mPhase2TtlsAdapter;
mPhase2Spinner.setAdapter(mPhase2Adapter);
}
mView.findViewById(R.id.l_phase2).setVisibility(View.VISIBLE);
@@ -1287,12 +1333,10 @@
private void setIdentityInvisible() {
mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
- mPhase2Spinner.setSelection(Phase2.NONE);
}
private void setPhase2Invisible() {
mView.findViewById(R.id.l_phase2).setVisibility(View.GONE);
- mPhase2Spinner.setSelection(Phase2.NONE);
}
private void setCaCertInvisible() {
diff --git a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
index 36c4455..2c6feac 100644
--- a/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiConnectionPreferenceController.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.os.Bundle;
+import android.util.FeatureFlagUtils;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
@@ -25,6 +26,7 @@
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.wifi.AccessPoint;
@@ -128,17 +130,31 @@
mPreference.refresh();
mPreference.setOrder(order);
- mPreference.setOnPreferenceClickListener(pref -> {
- Bundle args = new Bundle();
- mPreference.getAccessPoint().saveWifiState(args);
- new SubSettingLauncher(mPrefContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(args)
- .setSourceMetricsCategory(mMetricsCategory)
- .launch();
- return true;
- });
+ if (FeatureFlagUtils.isEnabled(mPrefContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ mPreference.setOnPreferenceClickListener(pref -> {
+ Bundle args = new Bundle();
+ mPreference.getAccessPoint().saveWifiState(args);
+ new SubSettingLauncher(mPrefContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsCategory)
+ .launch();
+ return true;
+ });
+ } else {
+ mPreference.setOnPreferenceClickListener(pref -> {
+ Bundle args = new Bundle();
+ mPreference.getAccessPoint().saveWifiState(args);
+ new SubSettingLauncher(mPrefContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(args)
+ .setSourceMetricsCategory(mMetricsCategory)
+ .launch();
+ return true;
+ });
+ }
mPreferenceGroup.addPreference(mPreference);
}
}
diff --git a/src/com/android/settings/wifi/WifiPickerActivity.java b/src/com/android/settings/wifi/WifiPickerActivity.java
index a590a0f..adfc7ec 100644
--- a/src/com/android/settings/wifi/WifiPickerActivity.java
+++ b/src/com/android/settings/wifi/WifiPickerActivity.java
@@ -16,6 +16,7 @@
package com.android.settings.wifi;
import android.content.Intent;
+import android.util.FeatureFlagUtils;
import androidx.preference.PreferenceFragmentCompat;
@@ -24,6 +25,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
+import com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2;
public class WifiPickerActivity extends SettingsActivity implements ButtonBarHandler {
@@ -39,9 +41,18 @@
@Override
protected boolean isValidFragment(String fragmentName) {
+ boolean isSavedAccessPointsWifiSettings;
+ if (FeatureFlagUtils.isEnabled(this, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ isSavedAccessPointsWifiSettings =
+ SavedAccessPointsWifiSettings2.class.getName().equals(fragmentName);
+ } else {
+ isSavedAccessPointsWifiSettings =
+ SavedAccessPointsWifiSettings.class.getName().equals(fragmentName);
+ }
+
if (WifiSettings.class.getName().equals(fragmentName)
|| WifiP2pSettings.class.getName().equals(fragmentName)
- || SavedAccessPointsWifiSettings.class.getName().equals(fragmentName)) {
+ || isSavedAccessPointsWifiSettings) {
return true;
}
return false;
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 28b668f..a5b380e 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -68,6 +68,7 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.SwitchBarController;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -954,12 +955,21 @@
? accessPoint.getTitle()
: context.getText(R.string.pref_title_network_details);
- new SubSettingLauncher(getContext())
- .setTitleText(title)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(pref.getExtras())
- .setSourceMetricsCategory(getMetricsCategory())
- .launch();
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ new SubSettingLauncher(getContext())
+ .setTitleText(title)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ } else {
+ new SubSettingLauncher(getContext())
+ .setTitleText(title)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(pref.getExtras())
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ }
}
private Network getCurrentWifiNetwork() {
diff --git a/src/com/android/settings/wifi/WifiSettings2.java b/src/com/android/settings/wifi/WifiSettings2.java
index 2d26cc4..f2cd1cf 100644
--- a/src/com/android/settings/wifi/WifiSettings2.java
+++ b/src/com/android/settings/wifi/WifiSettings2.java
@@ -190,7 +190,7 @@
}
private void addPreferences() {
- addPreferencesFromResource(R.xml.wifi_settings);
+ addPreferencesFromResource(R.xml.wifi_settings2);
mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);
diff --git a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
new file mode 100644
index 0000000..de831b7
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+import androidx.preference.Preference;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settingslib.wifi.AccessPoint;
+
+/**
+ * {@link BasePreferenceController} that launches Wi-Fi Easy Connect configurator flow
+ */
+public class AddDevicePreferenceController2 extends BasePreferenceController {
+
+ private static final String TAG = "AddDevicePreferenceController2";
+
+ private static final String KEY_ADD_DEVICE = "add_device_to_network";
+
+ private AccessPoint mAccessPoint;
+ private WifiManager mWifiManager;
+
+ public AddDevicePreferenceController2(Context context) {
+ super(context, KEY_ADD_DEVICE);
+
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ /**
+ * Initiate with an {@link AccessPoint}.
+ */
+ public AddDevicePreferenceController2 init(AccessPoint accessPoint) {
+ mAccessPoint = accessPoint;
+
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (WifiDppUtils.isSupportConfiguratorQrCodeScanner(mContext, mAccessPoint)) {
+ return AVAILABLE;
+ } else {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (KEY_ADD_DEVICE.equals(preference.getKey())) {
+ WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorQrCodeScanner());
+ return true; /* click is handled */
+ }
+
+ return false; /* click is not handled */
+ }
+
+ private void launchWifiDppConfiguratorQrCodeScanner() {
+ final Intent intent = WifiDppUtils.getConfiguratorQrCodeScannerIntentOrNull(mContext,
+ mWifiManager, mAccessPoint);
+
+ if (intent == null) {
+ Log.e(TAG, "Launch Wi-Fi QR code scanner with a wrong Wi-Fi network!");
+ } else {
+ mContext.startActivity(intent);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
new file mode 100644
index 0000000..1d6e457
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -0,0 +1,1207 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.settings.SettingsEnums;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.CountDownTimer;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.core.text.BidiFormatter;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.FeatureFlags;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.datausage.WifiDataUsageSummaryPreferenceController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settings.wifi.WifiDialog.WifiDialogListener;
+import com.android.settings.wifi.WifiUtils;
+import com.android.settings.wifi.dpp.WifiDppUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnPause;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.time.Duration;
+import java.util.StringJoiner;
+import java.util.stream.Collectors;
+
+/**
+ * Controller for logic pertaining to displaying Wifi information for the
+ * {@link WifiNetworkDetailsFragment}.
+ */
+public class WifiDetailPreferenceController2 extends AbstractPreferenceController
+ implements PreferenceControllerMixin, WifiDialogListener, LifecycleObserver, OnPause,
+ OnResume {
+
+ private static final String TAG = "WifiDetailsPrefCtrl2";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @VisibleForTesting
+ static final String KEY_HEADER = "connection_header";
+ @VisibleForTesting
+ static final String KEY_DATA_USAGE_HEADER = "status_header";
+ @VisibleForTesting
+ static final String KEY_BUTTONS_PREF = "buttons";
+ @VisibleForTesting
+ static final String KEY_SIGNAL_STRENGTH_PREF = "signal_strength";
+ @VisibleForTesting
+ static final String KEY_TX_LINK_SPEED = "tx_link_speed";
+ @VisibleForTesting
+ static final String KEY_RX_LINK_SPEED = "rx_link_speed";
+ @VisibleForTesting
+ static final String KEY_FREQUENCY_PREF = "frequency";
+ @VisibleForTesting
+ static final String KEY_SECURITY_PREF = "security";
+ @VisibleForTesting
+ static final String KEY_SSID_PREF = "ssid";
+ @VisibleForTesting
+ static final String KEY_MAC_ADDRESS_PREF = "mac_address";
+ @VisibleForTesting
+ static final String KEY_IP_ADDRESS_PREF = "ip_address";
+ @VisibleForTesting
+ static final String KEY_GATEWAY_PREF = "gateway";
+ @VisibleForTesting
+ static final String KEY_SUBNET_MASK_PREF = "subnet_mask";
+ @VisibleForTesting
+ static final String KEY_DNS_PREF = "dns";
+ @VisibleForTesting
+ static final String KEY_IPV6_CATEGORY = "ipv6_category";
+ @VisibleForTesting
+ static final String KEY_IPV6_ADDRESSES_PREF = "ipv6_addresses";
+
+ private static final int STATE_NONE = 1;
+ private static final int STATE_ENABLE_WIFI = 2;
+ private static final int STATE_ENABLE_WIFI_FAILED = 3;
+ private static final int STATE_CONNECTING = 4;
+ private static final int STATE_CONNECTED = 5;
+ private static final int STATE_FAILED = 6;
+ private static final int STATE_NOT_IN_RANGE = 7;
+ private static final int STATE_DISCONNECTED = 8;
+ private static final long TIMEOUT = Duration.ofSeconds(10).toMillis();
+
+ // Be static to avoid too much object not be reset.
+ @VisibleForTesting
+ static CountDownTimer sTimer;
+
+ private AccessPoint mAccessPoint;
+ private final ConnectivityManager mConnectivityManager;
+ private final PreferenceFragmentCompat mFragment;
+ private final Handler mHandler;
+ private LinkProperties mLinkProperties;
+ private Network mNetwork;
+ private NetworkInfo mNetworkInfo;
+ private NetworkCapabilities mNetworkCapabilities;
+ private int mRssiSignalLevel = -1;
+ private String[] mSignalStr;
+ private WifiConfiguration mWifiConfig;
+ private WifiInfo mWifiInfo;
+ private final WifiManager mWifiManager;
+ private final WifiTracker mWifiTracker;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private boolean mIsOutOfRange;
+ private boolean mIsEphemeral;
+ private boolean mConnected;
+ private int mConnectingState;
+ private WifiManager.ActionListener mConnectListener;
+
+ // UI elements - in order of appearance
+ private ActionButtonsPreference mButtonsPref;
+ private EntityHeaderController mEntityHeaderController;
+ private Preference mSignalStrengthPref;
+ private Preference mTxLinkSpeedPref;
+ private Preference mRxLinkSpeedPref;
+ private Preference mFrequencyPref;
+ private Preference mSecurityPref;
+ private Preference mSsidPref;
+ private Preference mMacAddressPref;
+ private Preference mIpAddressPref;
+ private Preference mGatewayPref;
+ private Preference mSubnetPref;
+ private Preference mDnsPref;
+ private PreferenceCategory mIpv6Category;
+ private Preference mIpv6AddressPref;
+ private Lifecycle mLifecycle;
+ Preference mDataUsageSummaryPref;
+ WifiDataUsageSummaryPreferenceController mSummaryHeaderController;
+
+ private final IconInjector mIconInjector;
+ private final IntentFilter mFilter;
+
+ // Passpoint information - cache it in case of losing these information after
+ // updateAccessPointFromScannedList(). For R2, we should update these data from
+ // WifiManager#getPasspointConfigurations() after users manage the passpoint profile.
+ private boolean mIsExpired;
+ private boolean mIsPasspointConfigurationR1;
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION:
+ if (!intent.getBooleanExtra(WifiManager.EXTRA_MULTIPLE_NETWORKS_CHANGED,
+ false /* defaultValue */)) {
+ // only one network changed
+ WifiConfiguration wifiConfiguration = intent
+ .getParcelableExtra(WifiManager.EXTRA_WIFI_CONFIGURATION);
+ if (mAccessPoint.matches(wifiConfiguration)) {
+ mWifiConfig = wifiConfiguration;
+ }
+ }
+ // fall through
+ case WifiManager.NETWORK_STATE_CHANGED_ACTION:
+ case WifiManager.RSSI_CHANGED_ACTION:
+ refreshPage();
+ break;
+ }
+ }
+ };
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities().addTransportType(TRANSPORT_WIFI).build();
+
+ // Must be run on the UI thread since it directly manipulates UI state.
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ if (network.equals(mNetwork) && !lp.equals(mLinkProperties)) {
+ mLinkProperties = lp;
+ refreshIpLayerInfo();
+ }
+ }
+
+ private boolean hasCapabilityChanged(NetworkCapabilities nc, int cap) {
+ // If this is the first time we get NetworkCapabilities, report that something changed.
+ if (mNetworkCapabilities == null) return true;
+
+ // nc can never be null, see ConnectivityService#callCallbackForRequest.
+ return mNetworkCapabilities.hasCapability(cap) != nc.hasCapability(cap);
+ }
+
+ private boolean hasPrivateDnsStatusChanged(NetworkCapabilities nc) {
+ // If this is the first time that WifiDetailPreferenceController2 gets
+ // NetworkCapabilities, report that something has changed and assign nc to
+ // mNetworkCapabilities in onCapabilitiesChanged. Note that the NetworkCapabilities
+ // from onCapabilitiesChanged() will never be null, so calling
+ // mNetworkCapabilities.isPrivateDnsBroken() would be safe next time.
+ if (mNetworkCapabilities == null) {
+ return true;
+ }
+
+ return mNetworkCapabilities.isPrivateDnsBroken() != nc.isPrivateDnsBroken();
+ }
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ // If the network just validated or lost Internet access or detected partial internet
+ // connectivity or private dns was broken, refresh network state. Don't do this on
+ // every NetworkCapabilities change because refreshEntityHeader sends IPCs to the
+ // system server from the UI thread, which can cause jank.
+ if (network.equals(mNetwork) && !nc.equals(mNetworkCapabilities)) {
+ if (hasPrivateDnsStatusChanged(nc)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_VALIDATED)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_CAPTIVE_PORTAL)
+ || hasCapabilityChanged(nc, NET_CAPABILITY_PARTIAL_CONNECTIVITY)) {
+ mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ refreshEntityHeader();
+ }
+ mNetworkCapabilities = nc;
+ refreshButtons();
+ refreshIpLayerInfo();
+ }
+ }
+
+ @Override
+ public void onLost(Network network) {
+ // Ephemeral network not a saved network, leave detail page once disconnected
+ if (mIsEphemeral && network.equals(mNetwork)) {
+ exitActivity();
+ }
+ }
+ };
+
+ @VisibleForTesting
+ final WifiTracker.WifiListener mWifiListener = new WifiTracker.WifiListener() {
+ /** Called when the state of Wifi has changed. */
+ public void onWifiStateChanged(int state) {
+ Log.d(TAG, "onWifiStateChanged(" + state + ")");
+ if (mConnectingState == STATE_ENABLE_WIFI && state == WifiManager.WIFI_STATE_ENABLED) {
+ updateConnectingState(STATE_CONNECTING);
+ } else if (mConnectingState != STATE_NONE && state == WifiManager.WIFI_STATE_DISABLED) {
+ // update as disconnected once Wi-Fi disabled since may not received
+ // onConnectedChanged for this case.
+ updateConnectingState(STATE_DISCONNECTED);
+ }
+ }
+
+ /** Called when the connection state of wifi has changed. */
+ public void onConnectedChanged() {
+ refreshPage();
+ }
+
+ /**
+ * Called to indicate the list of AccessPoints has been updated and
+ * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
+ */
+ public void onAccessPointsChanged() {
+ refreshPage();
+ }
+ };
+
+ /**
+ * To get an instance of {@link WifiDetailPreferenceController2}
+ */
+ public static WifiDetailPreferenceController2 newInstance(
+ AccessPoint accessPoint,
+ ConnectivityManager connectivityManager,
+ Context context,
+ PreferenceFragmentCompat fragment,
+ Handler handler,
+ Lifecycle lifecycle,
+ WifiManager wifiManager,
+ MetricsFeatureProvider metricsFeatureProvider) {
+ return new WifiDetailPreferenceController2(
+ accessPoint, connectivityManager, context, fragment, handler, lifecycle,
+ wifiManager, metricsFeatureProvider, new IconInjector(context));
+ }
+
+ @VisibleForTesting
+ /* package */ WifiDetailPreferenceController2(
+ AccessPoint accessPoint,
+ ConnectivityManager connectivityManager,
+ Context context,
+ PreferenceFragmentCompat fragment,
+ Handler handler,
+ Lifecycle lifecycle,
+ WifiManager wifiManager,
+ MetricsFeatureProvider metricsFeatureProvider,
+ IconInjector injector) {
+ super(context);
+
+ mAccessPoint = accessPoint;
+ mConnectivityManager = connectivityManager;
+ mFragment = fragment;
+ mHandler = handler;
+ mSignalStr = context.getResources().getStringArray(R.array.wifi_signal);
+ mWifiConfig = accessPoint.getConfig();
+ mWifiManager = wifiManager;
+ mMetricsFeatureProvider = metricsFeatureProvider;
+ mIconInjector = injector;
+
+ mFilter = new IntentFilter();
+ mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
+
+ mLifecycle = lifecycle;
+ lifecycle.addObserver(this);
+
+ mWifiTracker = WifiTrackerFactory.create(
+ mFragment.getActivity(),
+ mWifiListener,
+ mLifecycle,
+ true /*includeSaved*/,
+ true /*includeScans*/);
+ mConnected = mAccessPoint.isActive();
+ // When lost the network connection, WifiInfo/NetworkInfo will be clear. So causes we
+ // could not check if the AccessPoint is ephemeral. Need to cache it in first.
+ mIsEphemeral = mAccessPoint.isEphemeral();
+ mConnectingState = STATE_NONE;
+ mConnectListener = new WifiManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ // Do nothing
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ updateConnectingState(STATE_FAILED);
+ }
+ };
+
+ mIsExpired = mAccessPoint.isExpired();
+ mIsPasspointConfigurationR1 = mAccessPoint.isPasspointConfigurationR1();
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ // Returns null since this controller contains more than one Preference
+ return null;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+
+ setupEntityHeader(screen);
+
+ mButtonsPref = ((ActionButtonsPreference) screen.findPreference(KEY_BUTTONS_PREF))
+ .setButton1Text(R.string.forget)
+ .setButton1Icon(R.drawable.ic_settings_delete)
+ .setButton1OnClickListener(view -> forgetNetwork())
+ .setButton2Text(R.string.wifi_sign_in_button_text)
+ .setButton2Icon(R.drawable.ic_settings_sign_in)
+ .setButton2OnClickListener(view -> signIntoNetwork())
+ .setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3OnClickListener(view -> connectNetwork())
+ .setButton3Enabled(true)
+ .setButton4Text(R.string.share)
+ .setButton4Icon(R.drawable.ic_qrcode_24dp)
+ .setButton4OnClickListener(view -> shareNetwork());
+
+ if (isPasspointConfigurationR1Expired()) {
+ // Hide Connect button.
+ mButtonsPref.setButton3Visible(false);
+ }
+
+ mSignalStrengthPref = screen.findPreference(KEY_SIGNAL_STRENGTH_PREF);
+ mTxLinkSpeedPref = screen.findPreference(KEY_TX_LINK_SPEED);
+ mRxLinkSpeedPref = screen.findPreference(KEY_RX_LINK_SPEED);
+ mFrequencyPref = screen.findPreference(KEY_FREQUENCY_PREF);
+ mSecurityPref = screen.findPreference(KEY_SECURITY_PREF);
+
+ mSsidPref = screen.findPreference(KEY_SSID_PREF);
+ mMacAddressPref = screen.findPreference(KEY_MAC_ADDRESS_PREF);
+ mIpAddressPref = screen.findPreference(KEY_IP_ADDRESS_PREF);
+ mGatewayPref = screen.findPreference(KEY_GATEWAY_PREF);
+ mSubnetPref = screen.findPreference(KEY_SUBNET_MASK_PREF);
+ mDnsPref = screen.findPreference(KEY_DNS_PREF);
+
+ mIpv6Category = screen.findPreference(KEY_IPV6_CATEGORY);
+ mIpv6AddressPref = screen.findPreference(KEY_IPV6_ADDRESSES_PREF);
+
+ mSecurityPref.setSummary(mAccessPoint.getSecurityString(/* concise */ false));
+ }
+
+ private void setupEntityHeader(PreferenceScreen screen) {
+ LayoutPreference headerPref = screen.findPreference(KEY_HEADER);
+
+ if (usingDataUsageHeader(mContext)) {
+ headerPref.setVisible(false);
+ mDataUsageSummaryPref = screen.findPreference(KEY_DATA_USAGE_HEADER);
+ mDataUsageSummaryPref.setVisible(true);
+ mSummaryHeaderController =
+ new WifiDataUsageSummaryPreferenceController(mFragment.getActivity(),
+ mLifecycle, (PreferenceFragmentCompat) mFragment, mAccessPoint.getSsid());
+ return;
+ }
+
+ mEntityHeaderController =
+ EntityHeaderController.newInstance(
+ mFragment.getActivity(), mFragment,
+ headerPref.findViewById(R.id.entity_header));
+
+ ImageView iconView = headerPref.findViewById(R.id.entity_header_icon);
+
+ iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+ mEntityHeaderController.setLabel(mAccessPoint.getTitle());
+ }
+
+ private void refreshEntityHeader() {
+ if (usingDataUsageHeader(mContext)) {
+ mSummaryHeaderController.updateState(mDataUsageSummaryPref);
+ } else {
+ String summary;
+ if (isPasspointConfigurationR1Expired()) {
+ // Not able to get summary from AccessPoint because we may lost
+ // PasspointConfiguration information after updateAccessPointFromScannedList().
+ summary = mContext.getResources().getString(
+ com.android.settingslib.R.string.wifi_passpoint_expired);
+ } else {
+ summary = mAccessPoint.getSettingsSummary(true /* convertSavedAsDisconnected */);
+ }
+
+ mEntityHeaderController
+ .setSummary(summary)
+ .setRecyclerView(mFragment.getListView(), mLifecycle)
+ .done(mFragment.getActivity(), true /* rebind */);
+ }
+ }
+
+ private void updateNetworkInfo() {
+ mNetwork = mWifiManager.getCurrentNetwork();
+ mLinkProperties = mConnectivityManager.getLinkProperties(mNetwork);
+ mNetworkCapabilities = mConnectivityManager.getNetworkCapabilities(mNetwork);
+ }
+
+ @Override
+ public void onResume() {
+ // Ensure mNetwork is set before any callbacks above are delivered, since our
+ // NetworkCallback only looks at changes to mNetwork.
+ updateNetworkInfo();
+ refreshPage();
+ mContext.registerReceiver(mReceiver, mFilter);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback,
+ mHandler);
+ }
+
+ @Override
+ public void onPause() {
+ mNetwork = null;
+ mLinkProperties = null;
+ mNetworkCapabilities = null;
+ mNetworkInfo = null;
+ mWifiInfo = null;
+ mContext.unregisterReceiver(mReceiver);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ private void refreshPage() {
+ if (!updateAccessPoint()) {
+ return;
+ }
+
+ Log.d(TAG, "Update UI!");
+
+ // refresh header
+ refreshEntityHeader();
+
+ // refresh Buttons
+ refreshButtons();
+
+ // Update Connection Header icon and Signal Strength Preference
+ refreshRssiViews();
+ // Frequency Pref
+ refreshFrequency();
+ // Transmit Link Speed Pref
+ refreshTxSpeed();
+ // Receive Link Speed Pref
+ refreshRxSpeed();
+ // IP related information
+ refreshIpLayerInfo();
+ // SSID Pref
+ refreshSsid();
+ // MAC Address Pref
+ refreshMacAddress();
+ }
+
+ @VisibleForTesting
+ boolean updateAccessPoint() {
+ boolean changed = false;
+ // remember mIsOutOfRange as old before updated
+ boolean oldState = mIsOutOfRange;
+ updateAccessPointFromScannedList();
+
+ if (mAccessPoint.isActive()) {
+ updateNetworkInfo();
+ mNetworkInfo = mConnectivityManager.getNetworkInfo(mNetwork);
+ mWifiInfo = mWifiManager.getConnectionInfo();
+ if (mNetwork == null || mNetworkInfo == null || mWifiInfo == null) {
+ // Once connected, can't get mNetwork immediately, return false and wait for
+ // next time to update UI. also reset {@code mIsOutOfRange}
+ mIsOutOfRange = oldState;
+ return false;
+ }
+ changed |= mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
+ }
+
+ // signal level changed
+ changed |= mRssiSignalLevel != mAccessPoint.getLevel();
+ // In/Out of range changed
+ changed |= oldState != mIsOutOfRange;
+ // connect state changed
+ if (mConnected != mAccessPoint.isActive()) {
+ mConnected = mAccessPoint.isActive();
+ changed = true;
+ updateConnectingState(mAccessPoint.isActive() ? STATE_CONNECTED : STATE_DISCONNECTED);
+ }
+
+ return changed;
+ }
+
+ private void updateAccessPointFromScannedList() {
+ mIsOutOfRange = true;
+
+ for (AccessPoint ap : mWifiTracker.getAccessPoints()) {
+ if (mAccessPoint.matches(ap)) {
+ mAccessPoint = ap;
+ mWifiConfig = ap.getConfig();
+ mIsOutOfRange = !mAccessPoint.isReachable();
+ return;
+ }
+ }
+ }
+
+ private void exitActivity() {
+ if (DEBUG) {
+ Log.d(TAG, "Exiting the WifiNetworkDetailsPage");
+ }
+ mFragment.getActivity().finish();
+ }
+
+ private void refreshRssiViews() {
+ int signalLevel = mAccessPoint.getLevel();
+
+ // Disappears signal view if not in range. e.g. for saved networks.
+ if (mIsOutOfRange) {
+ mSignalStrengthPref.setVisible(false);
+ mRssiSignalLevel = -1;
+ return;
+ }
+
+ if (mRssiSignalLevel == signalLevel) {
+ return;
+ }
+ mRssiSignalLevel = signalLevel;
+ Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
+
+ if (mEntityHeaderController != null) {
+ mEntityHeaderController
+ .setIcon(redrawIconForHeader(wifiIcon)).done(mFragment.getActivity(),
+ true /* rebind */);
+ }
+
+ Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
+ wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
+ mSignalStrengthPref.setIcon(wifiIconDark);
+
+ mSignalStrengthPref.setSummary(mSignalStr[mRssiSignalLevel]);
+ mSignalStrengthPref.setVisible(true);
+ }
+
+ private Drawable redrawIconForHeader(Drawable original) {
+ final int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ final int actualWidth = original.getMinimumWidth();
+ final int actualHeight = original.getMinimumHeight();
+
+ if ((actualWidth == iconSize && actualHeight == iconSize)
+ || !VectorDrawable.class.isInstance(original)) {
+ return original;
+ }
+
+ // clear tint list to make sure can set 87% black after enlarge
+ original.setTintList(null);
+
+ // enlarge icon size
+ final Bitmap bitmap = Utils.createBitmap(original,
+ iconSize /*width*/,
+ iconSize /*height*/);
+ Drawable newIcon = new BitmapDrawable(null /*resource*/, bitmap);
+
+ // config color for 87% black after enlarge
+ newIcon.setTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+
+ return newIcon;
+ }
+
+ private void refreshFrequency() {
+ if (mWifiInfo == null) {
+ mFrequencyPref.setVisible(false);
+ return;
+ }
+
+ final int frequency = mWifiInfo.getFrequency();
+ String band = null;
+ if (frequency >= AccessPoint.LOWER_FREQ_24GHZ
+ && frequency < AccessPoint.HIGHER_FREQ_24GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_24ghz);
+ } else if (frequency >= AccessPoint.LOWER_FREQ_5GHZ
+ && frequency < AccessPoint.HIGHER_FREQ_5GHZ) {
+ band = mContext.getResources().getString(R.string.wifi_band_5ghz);
+ } else {
+ Log.e(TAG, "Unexpected frequency " + frequency);
+ // Connecting state is unstable, make it disappeared if unexpected
+ if (mConnectingState == STATE_CONNECTING) {
+ mFrequencyPref.setVisible(false);
+ }
+ return;
+ }
+ mFrequencyPref.setSummary(band);
+ mFrequencyPref.setVisible(true);
+ }
+
+ private void refreshTxSpeed() {
+ if (mWifiInfo == null) {
+ mTxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int txLinkSpeedMbps = mWifiInfo.getTxLinkSpeedMbps();
+ mTxLinkSpeedPref.setVisible(txLinkSpeedMbps >= 0);
+ mTxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.tx_link_speed, mWifiInfo.getTxLinkSpeedMbps()));
+ }
+
+ private void refreshRxSpeed() {
+ if (mWifiInfo == null) {
+ mRxLinkSpeedPref.setVisible(false);
+ return;
+ }
+
+ int rxLinkSpeedMbps = mWifiInfo.getRxLinkSpeedMbps();
+ mRxLinkSpeedPref.setVisible(rxLinkSpeedMbps >= 0);
+ mRxLinkSpeedPref.setSummary(mContext.getString(
+ R.string.rx_link_speed, mWifiInfo.getRxLinkSpeedMbps()));
+ }
+
+ private void refreshSsid() {
+ if (mAccessPoint.isPasspoint() || mAccessPoint.isOsuProvider()) {
+ mSsidPref.setVisible(true);
+ mSsidPref.setSummary(mAccessPoint.getSsidStr());
+ } else {
+ mSsidPref.setVisible(false);
+ }
+ }
+
+ private void refreshMacAddress() {
+ String macAddress = getMacAddress();
+ if (macAddress == null) {
+ mMacAddressPref.setVisible(false);
+ return;
+ }
+
+ mMacAddressPref.setVisible(true);
+ if (macAddress.equals(WifiInfo.DEFAULT_MAC_ADDRESS)) {
+ mMacAddressPref.setSummary(R.string.device_info_not_available);
+ } else {
+ mMacAddressPref.setSummary(macAddress);
+ }
+
+ // MAC Address Pref Title
+ refreshMacTitle();
+ }
+
+ private String getMacAddress() {
+ if (mWifiInfo != null) {
+ // get MAC address from connected network information
+ return mWifiInfo.getMacAddress();
+ }
+
+ // return randomized MAC address
+ if (mWifiConfig != null && mWifiConfig.macRandomizationSetting
+ == WifiConfiguration.RANDOMIZATION_PERSISTENT) {
+ return mWifiConfig.getRandomizedMacAddress().toString();
+ }
+
+ // return device MAC address
+ final String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+ if (macAddresses != null && macAddresses.length > 0) {
+ return macAddresses[0];
+ }
+
+ Log.e(TAG, "Can't get device MAC address!");
+ return null;
+ }
+
+ private void updatePreference(Preference pref, String detailText) {
+ if (!TextUtils.isEmpty(detailText)) {
+ pref.setSummary(detailText);
+ pref.setVisible(true);
+ } else {
+ pref.setVisible(false);
+ }
+ }
+
+ private void refreshButtons() {
+ // Ephemeral network won't be removed permanently, but be putted in blacklist.
+ mButtonsPref.setButton1Text(
+ mIsEphemeral ? R.string.wifi_disconnect_button_text : R.string.forget);
+
+ boolean canForgetNetwork = canForgetNetwork();
+ boolean canSignIntoNetwork = canSignIntoNetwork();
+ boolean canConnectNetwork = canConnectNetwork() && !isPasspointConfigurationR1Expired();
+ boolean canShareNetwork = canShareNetwork();
+
+ mButtonsPref.setButton1Visible(canForgetNetwork);
+ mButtonsPref.setButton2Visible(canSignIntoNetwork);
+ mButtonsPref.setButton3Visible(canConnectNetwork);
+ mButtonsPref.setButton4Visible(canShareNetwork);
+ mButtonsPref.setVisible(canForgetNetwork
+ || canSignIntoNetwork
+ || canConnectNetwork
+ || canShareNetwork);
+ }
+
+ private boolean canConnectNetwork() {
+ // Display connect button for disconnected AP even not in the range.
+ return !mAccessPoint.isActive();
+ }
+
+ private boolean isPasspointConfigurationR1Expired() {
+ return mIsPasspointConfigurationR1 && mIsExpired;
+ }
+
+ private void refreshIpLayerInfo() {
+ // Hide IP layer info if not a connected network.
+ if (!mAccessPoint.isActive() || mNetwork == null || mLinkProperties == null) {
+ mIpAddressPref.setVisible(false);
+ mSubnetPref.setVisible(false);
+ mGatewayPref.setVisible(false);
+ mDnsPref.setVisible(false);
+ mIpv6Category.setVisible(false);
+ return;
+ }
+
+ // Find IPv4 and IPv6 addresses.
+ String ipv4Address = null;
+ String subnet = null;
+ StringJoiner ipv6Addresses = new StringJoiner("\n");
+
+ for (LinkAddress addr : mLinkProperties.getLinkAddresses()) {
+ if (addr.getAddress() instanceof Inet4Address) {
+ ipv4Address = addr.getAddress().getHostAddress();
+ subnet = ipv4PrefixLengthToSubnetMask(addr.getPrefixLength());
+ } else if (addr.getAddress() instanceof Inet6Address) {
+ ipv6Addresses.add(addr.getAddress().getHostAddress());
+ }
+ }
+
+ // Find IPv4 default gateway.
+ String gateway = null;
+ for (RouteInfo routeInfo : mLinkProperties.getRoutes()) {
+ if (routeInfo.isIPv4Default() && routeInfo.hasGateway()) {
+ gateway = routeInfo.getGateway().getHostAddress();
+ break;
+ }
+ }
+
+ // Find all (IPv4 and IPv6) DNS addresses.
+ String dnsServers = mLinkProperties.getDnsServers().stream()
+ .map(InetAddress::getHostAddress)
+ .collect(Collectors.joining("\n"));
+
+ // Update UI.
+ updatePreference(mIpAddressPref, ipv4Address);
+ updatePreference(mSubnetPref, subnet);
+ updatePreference(mGatewayPref, gateway);
+ updatePreference(mDnsPref, dnsServers);
+
+ if (ipv6Addresses.length() > 0) {
+ mIpv6AddressPref.setSummary(
+ BidiFormatter.getInstance().unicodeWrap(ipv6Addresses.toString()));
+ mIpv6Category.setVisible(true);
+ } else {
+ mIpv6Category.setVisible(false);
+ }
+ }
+
+ private static String ipv4PrefixLengthToSubnetMask(int prefixLength) {
+ try {
+ InetAddress all = InetAddress.getByAddress(
+ new byte[]{(byte) 255, (byte) 255, (byte) 255, (byte) 255});
+ return NetworkUtils.getNetworkPart(all, prefixLength).getHostAddress();
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns whether the network represented by this preference can be forgotten.
+ */
+ private boolean canForgetNetwork() {
+ return (mWifiInfo != null && mWifiInfo.isEphemeral()) || canModifyNetwork()
+ || mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig();
+ }
+
+ /**
+ * Returns whether the network represented by this preference can be modified.
+ */
+ public boolean canModifyNetwork() {
+ return mWifiConfig != null && !WifiUtils.isNetworkLockedDown(mContext, mWifiConfig);
+ }
+
+ /**
+ * Returns whether the user can sign into the network represented by this preference.
+ */
+ private boolean canSignIntoNetwork() {
+ return mAccessPoint.isActive() && WifiUtils.canSignIntoNetwork(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns whether the user can share the network represented by this preference with QR code.
+ */
+ private boolean canShareNetwork() {
+ return mAccessPoint.getConfig() != null
+ && WifiDppUtils.isSupportConfiguratorQrCodeGenerator(mContext, mAccessPoint);
+ }
+
+ /**
+ * Forgets the wifi network associated with this preference.
+ */
+ private void forgetNetwork() {
+ if (mWifiInfo != null && mWifiInfo.isEphemeral()) {
+ mWifiManager.disableEphemeralNetwork(mWifiInfo.getSSID());
+ } else if (mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig()) {
+ // Post a dialog to confirm if user really want to forget the passpoint network.
+ showConfirmForgetDialog();
+ return;
+ } else if (mWifiConfig != null) {
+ mWifiManager.forget(mWifiConfig.networkId, null /* action listener */);
+ }
+
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
+ mFragment.getActivity().finish();
+ }
+
+ @VisibleForTesting
+ protected void showConfirmForgetDialog() {
+ final AlertDialog dialog = new AlertDialog.Builder(mContext)
+ .setPositiveButton(R.string.forget, ((dialog1, which) -> {
+ try {
+ mWifiManager.removePasspointConfiguration(mAccessPoint.getPasspointFqdn());
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to remove Passpoint configuration for "
+ + mAccessPoint.getPasspointFqdn());
+ }
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
+ mFragment.getActivity().finish();
+ }))
+ .setNegativeButton(R.string.cancel, null /* listener */)
+ .setTitle(R.string.wifi_forget_dialog_title)
+ .setMessage(R.string.forget_passpoint_dialog_message)
+ .create();
+ dialog.show();
+ }
+
+ /**
+ * Show QR code to share the network represented by this preference.
+ */
+ private void launchWifiDppConfiguratorActivity() {
+ final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(mContext,
+ mWifiManager, mAccessPoint);
+
+ if (intent == null) {
+ Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!");
+ } else {
+ mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
+ SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE,
+ SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
+ /* key */ null,
+ /* value */ Integer.MIN_VALUE);
+
+ mContext.startActivity(intent);
+ }
+ }
+
+ /**
+ * Share the wifi network with QR code.
+ */
+ private void shareNetwork() {
+ WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
+ }
+
+ /**
+ * Sign in to the captive portal found on this wifi network associated with this preference.
+ */
+ private void signIntoNetwork() {
+ mMetricsFeatureProvider.action(
+ mFragment.getActivity(), SettingsEnums.ACTION_WIFI_SIGNIN);
+ mConnectivityManager.startCaptivePortalApp(mNetwork);
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ mWifiManager.save(dialog.getController().getConfig(), new WifiManager.ActionListener() {
+ @Override
+ public void onSuccess() {
+ }
+
+ @Override
+ public void onFailure(int reason) {
+ Activity activity = mFragment.getActivity();
+ if (activity != null) {
+ Toast.makeText(activity,
+ R.string.wifi_failed_save_message,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Wrapper for testing compatibility.
+ */
+ @VisibleForTesting
+ static class IconInjector {
+ private final Context mContext;
+
+ IconInjector(Context context) {
+ mContext = context;
+ }
+
+ public Drawable getIcon(int level) {
+ return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
+ }
+ }
+
+ private boolean usingDataUsageHeader(Context context) {
+ return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER);
+ }
+
+ @VisibleForTesting
+ void connectNetwork() {
+ final Activity activity = mFragment.getActivity();
+ // error handling, connected/saved network should have mWifiConfig.
+ if (mWifiConfig == null) {
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // init state before connect
+ mConnectingState = STATE_NONE;
+
+ if (mWifiManager.isWifiEnabled()) {
+ updateConnectingState(STATE_CONNECTING);
+ } else {
+ // Enable Wi-Fi automatically to connect AP
+ updateConnectingState(STATE_ENABLE_WIFI);
+ }
+ }
+
+ private void updateConnectingState(int state) {
+ final Activity activity = mFragment.getActivity();
+ Log.d(TAG, "updateConnectingState from " + mConnectingState + " to " + state);
+ switch (mConnectingState) {
+ case STATE_NONE:
+ case STATE_ENABLE_WIFI:
+ if (state == STATE_ENABLE_WIFI) {
+ Log.d(TAG, "Turn on Wi-Fi automatically!");
+ updateConnectedButton(STATE_ENABLE_WIFI);
+ Toast.makeText(activity,
+ R.string.wifi_turned_on_message,
+ Toast.LENGTH_SHORT).show();
+ mWifiManager.setWifiEnabled(true);
+ // start timer for error handling
+ startTimer();
+ } else if (state == STATE_CONNECTING) {
+ Log.d(TAG, "connecting...");
+ updateConnectedButton(STATE_CONNECTING);
+ if (mAccessPoint.isPasspoint()) {
+ mWifiManager.connect(mWifiConfig, mConnectListener);
+ } else {
+ mWifiManager.connect(mWifiConfig.networkId, mConnectListener);
+ }
+ // start timer for error handling since framework didn't call back if failed
+ startTimer();
+ } else if (state == STATE_ENABLE_WIFI_FAILED) {
+ Log.e(TAG, "Wi-Fi failed to enable network!");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_ENABLE_WIFI_FAILED);
+ }
+ // Do not break here for disconnected event.
+ case STATE_CONNECTED:
+ if (state == STATE_DISCONNECTED) {
+ Log.d(TAG, "disconnected");
+ // reset state
+ state = STATE_NONE;
+ updateConnectedButton(STATE_DISCONNECTED);
+ refreshPage();
+ // clear for getting MAC Address from saved configuration
+ mWifiInfo = null;
+ }
+ break;
+ case STATE_CONNECTING:
+ if (state == STATE_CONNECTED) {
+ Log.d(TAG, "connected");
+ stopTimer();
+ updateConnectedButton(STATE_CONNECTED);
+ Toast.makeText(activity,
+ mContext.getString(R.string.wifi_connected_to_message,
+ mAccessPoint.getTitle()),
+ Toast.LENGTH_SHORT).show();
+
+ refreshPage();
+ } else if (state == STATE_NOT_IN_RANGE) {
+ Log.d(TAG, "AP not in range");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_not_in_range_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_NOT_IN_RANGE);
+ } else if (state == STATE_FAILED) {
+ Log.d(TAG, "failed");
+ stopTimer();
+ // reset state
+ state = STATE_NONE;
+ Toast.makeText(activity,
+ R.string.wifi_failed_connect_message,
+ Toast.LENGTH_SHORT).show();
+ updateConnectedButton(STATE_FAILED);
+ }
+ break;
+ default:
+ Log.e(TAG, "Invalid state : " + mConnectingState);
+ // don't update invalid state
+ return;
+ }
+
+ mConnectingState = state;
+ }
+
+ private void updateConnectedButton(int state) {
+ switch (state) {
+ case STATE_ENABLE_WIFI:
+ case STATE_CONNECTING:
+ mButtonsPref.setButton3Text(R.string.wifi_connecting)
+ .setButton3Enabled(false);
+ break;
+ case STATE_CONNECTED:
+ // init button state and set as invisible
+ mButtonsPref.setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3Enabled(true)
+ .setButton3Visible(false);
+ break;
+ case STATE_DISCONNECTED:
+ case STATE_NOT_IN_RANGE:
+ case STATE_FAILED:
+ case STATE_ENABLE_WIFI_FAILED:
+ if (isPasspointConfigurationR1Expired()) {
+ // Hide Connect button.
+ mButtonsPref.setButton3Visible(false);
+ } else {
+ mButtonsPref.setButton3Text(R.string.wifi_connect)
+ .setButton3Icon(R.drawable.ic_settings_wireless)
+ .setButton3Enabled(true)
+ .setButton3Visible(true);
+ }
+ break;
+ default:
+ Log.e(TAG, "Invalid connect button state : " + state);
+ break;
+ }
+ }
+
+ private void startTimer() {
+ if (sTimer != null) {
+ stopTimer();
+ }
+
+ sTimer = new CountDownTimer(TIMEOUT, TIMEOUT + 1) {
+ @Override
+ public void onTick(long millisUntilFinished) {
+ // Do nothing
+ }
+ @Override
+ public void onFinish() {
+ if (mFragment == null || mFragment.getActivity() == null) {
+ Log.d(TAG, "Ignore timeout since activity not exist!");
+ return;
+ }
+ Log.e(TAG, "Timeout for state:" + mConnectingState);
+ if (mConnectingState == STATE_ENABLE_WIFI) {
+ updateConnectingState(STATE_ENABLE_WIFI_FAILED);
+ } else if (mConnectingState == STATE_CONNECTING) {
+ updateAccessPointFromScannedList();
+ if (mIsOutOfRange) {
+ updateConnectingState(STATE_NOT_IN_RANGE);
+ } else {
+ updateConnectingState(STATE_FAILED);
+ }
+ }
+ }
+ };
+ sTimer.start();
+ }
+
+ private void stopTimer() {
+ if (sTimer == null) return;
+
+ sTimer.cancel();
+ sTimer = null;
+ }
+
+ private void refreshMacTitle() {
+ if (mWifiConfig == null) {
+ return;
+ }
+
+ // For saved Passpoint network, framework doesn't have the field to keep the MAC choice
+ // persistently, so Passpoint network will always use the default value so far, which is
+ // randomized MAC address, so don't need to modify title.
+ if (mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig()) {
+ return;
+ }
+
+ mMacAddressPref.setTitle(
+ (mWifiConfig.macRandomizationSetting
+ == WifiConfiguration.RANDOMIZATION_PERSISTENT)
+ ? R.string.wifi_advanced_randomized_mac_address_title
+ : R.string.wifi_advanced_device_mac_address_title);
+
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
new file mode 100644
index 0000000..99967dc
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.DropDownPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * {@link AbstractPreferenceController} that controls whether the wifi network is metered or not
+ */
+public class WifiMeteredPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, WifiDialog.WifiDialogListener {
+
+ private static final String KEY_WIFI_METERED = "metered";
+ private WifiConfiguration mWifiConfiguration;
+ private WifiManager mWifiManager;
+ private Preference mPreference;
+
+ public WifiMeteredPreferenceController2(Context context, WifiConfiguration wifiConfiguration) {
+ super(context, KEY_WIFI_METERED);
+ mWifiConfiguration = wifiConfiguration;
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final DropDownPreference dropDownPreference = (DropDownPreference) preference;
+ final int meteredOverride = getMeteredOverride();
+ dropDownPreference.setValue(Integer.toString(meteredOverride));
+ updateSummary(dropDownPreference, meteredOverride);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mWifiConfiguration != null) {
+ mWifiConfiguration.meteredOverride = Integer.parseInt((String) newValue);
+ }
+ mWifiManager.updateNetwork(mWifiConfiguration);
+ // Stage the backup of the SettingsProvider package which backs this up
+ BackupManager.dataChanged("com.android.providers.settings");
+ updateSummary((DropDownPreference) preference, getMeteredOverride());
+ return true;
+ }
+
+ @VisibleForTesting
+ int getMeteredOverride() {
+ if (mWifiConfiguration != null) {
+ // Wrap the meteredOverride since robolectric cannot recognize it
+ return mWifiConfiguration.meteredOverride;
+ }
+ return WifiConfiguration.METERED_OVERRIDE_NONE;
+ }
+
+ private void updateSummary(DropDownPreference preference, int meteredOverride) {
+ preference.setSummary(preference.getEntries()[meteredOverride]);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ final WifiConfiguration newConfig = dialog.getController().getConfig();
+ if (newConfig == null || mWifiConfiguration == null) {
+ return;
+ }
+
+ if (newConfig.meteredOverride != mWifiConfiguration.meteredOverride) {
+ mWifiConfiguration = newConfig;
+ onPreferenceChange(mPreference, String.valueOf(newConfig.meteredOverride));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
new file mode 100644
index 0000000..5eb4b28
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.android.settings.wifi.WifiSettings.WIFI_DIALOG_ID;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.wifi.WifiConfigUiBase;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtilsInternal;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.wifi.AccessPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Detail page for the currently connected wifi network.
+ *
+ * <p>The AccessPoint should be saved to the intent Extras when launching this class via
+ * {@link AccessPoint#saveWifiState(Bundle)} in order to properly render this page.
+ */
+public class WifiNetworkDetailsFragment2 extends DashboardFragment implements
+ WifiDialog.WifiDialogListener {
+
+ private static final String TAG = "WifiNetworkDetailsFrg2";
+
+ private AccessPoint mAccessPoint;
+ private WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
+ private List<WifiDialog.WifiDialogListener> mWifiDialogListeners = new ArrayList<>();
+
+ @Override
+ public void onAttach(Context context) {
+ mAccessPoint = new AccessPoint(context, getArguments());
+ super.onAttach(context);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_NETWORK_DETAILS;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.wifi_network_details_fragment;
+ }
+
+ @Override
+ public int getDialogMetricsCategory(int dialogId) {
+ if (dialogId == WIFI_DIALOG_ID) {
+ return SettingsEnums.DIALOG_WIFI_AP_EDIT;
+ }
+ return 0;
+ }
+
+ @Override
+ public Dialog onCreateDialog(int dialogId) {
+ if (getActivity() == null || mWifiDetailPreferenceController2 == null
+ || mAccessPoint == null) {
+ return null;
+ }
+ return WifiDialog.createModal(getActivity(), this, mAccessPoint,
+ WifiConfigUiBase.MODE_MODIFY);
+ }
+
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
+ item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case Menu.FIRST:
+ if (!mWifiDetailPreferenceController2.canModifyNetwork()) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
+ RestrictedLockUtilsInternal.getDeviceOwner(getContext()));
+ } else {
+ showDialog(WIFI_DIALOG_ID);
+ }
+ return true;
+ default:
+ return super.onOptionsItemSelected(menuItem);
+ }
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+
+ mWifiDetailPreferenceController2 = WifiDetailPreferenceController2.newInstance(
+ mAccessPoint,
+ cm,
+ context,
+ this,
+ new Handler(Looper.getMainLooper()), // UI thread.
+ getSettingsLifecycle(),
+ context.getSystemService(WifiManager.class),
+ mMetricsFeatureProvider);
+
+ controllers.add(mWifiDetailPreferenceController2);
+ controllers.add(new AddDevicePreferenceController2(context).init(mAccessPoint));
+
+ final WifiMeteredPreferenceController2 meteredPreferenceController2 =
+ new WifiMeteredPreferenceController2(context, mAccessPoint.getConfig());
+ controllers.add(meteredPreferenceController2);
+
+ final WifiPrivacyPreferenceController2 privacyController2 =
+ new WifiPrivacyPreferenceController2(context);
+ privacyController2.setWifiConfiguration(mAccessPoint.getConfig());
+ privacyController2.setIsEphemeral(mAccessPoint.isEphemeral());
+ privacyController2.setIsPasspoint(
+ mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig());
+ controllers.add(privacyController2);
+
+ // Sets callback listener for wifi dialog.
+ mWifiDialogListeners.add(mWifiDetailPreferenceController2);
+ mWifiDialogListeners.add(privacyController2);
+ mWifiDialogListeners.add(meteredPreferenceController2);
+
+ return controllers;
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ for (WifiDialog.WifiDialogListener listener : mWifiDialogListeners) {
+ listener.onSubmit(dialog);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
new file mode 100644
index 0000000..d85b607
--- /dev/null
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.DropDownPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.wifi.WifiDialog;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * {@link AbstractPreferenceController} that controls whether the wifi network is mac randomized
+ * or not
+ */
+public class WifiPrivacyPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceChangeListener, WifiDialog.WifiDialogListener {
+
+ private static final String KEY_WIFI_PRIVACY = "privacy";
+ private WifiConfiguration mWifiConfiguration;
+ private WifiManager mWifiManager;
+ private boolean mIsEphemeral = false;
+ private boolean mIsPasspoint = false;
+ private Preference mPreference;
+
+ public WifiPrivacyPreferenceController2(Context context) {
+ super(context, KEY_WIFI_PRIVACY);
+ mWifiConfiguration = null;
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ public void setWifiConfiguration(WifiConfiguration wifiConfiguration) {
+ mWifiConfiguration = wifiConfiguration;
+ }
+
+ public void setIsEphemeral(boolean isEphemeral) {
+ mIsEphemeral = isEphemeral;
+ }
+
+ public void setIsPasspoint(boolean isPasspoint) {
+ mIsPasspoint = isPasspoint;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mWifiManager.isConnectedMacRandomizationSupported()
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ final DropDownPreference dropDownPreference = (DropDownPreference) preference;
+ final int randomizationLevel = getRandomizationValue();
+ dropDownPreference.setValue(Integer.toString(randomizationLevel));
+ updateSummary(dropDownPreference, randomizationLevel);
+
+ // Makes preference not selectable, when this is a ephemeral network.
+ if (mIsEphemeral || mIsPasspoint) {
+ preference.setSelectable(false);
+ dropDownPreference.setSummary(R.string.wifi_privacy_settings_ephemeral_summary);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mWifiConfiguration != null) {
+ mWifiConfiguration.macRandomizationSetting = Integer.parseInt((String) newValue);
+ mWifiManager.updateNetwork(mWifiConfiguration);
+
+ // To activate changing, we need to reconnect network. WiFi will auto connect to
+ // current network after disconnect(). Only needed when this is connected network.
+ final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo != null && wifiInfo.getNetworkId() == mWifiConfiguration.networkId) {
+ mWifiManager.disconnect();
+ }
+ }
+ updateSummary((DropDownPreference) preference, Integer.parseInt((String) newValue));
+ return true;
+ }
+
+ @VisibleForTesting
+ int getRandomizationValue() {
+ if (mWifiConfiguration != null) {
+ return mWifiConfiguration.macRandomizationSetting;
+ }
+ return WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ }
+
+ private static final int PREF_RANDOMIZATION_PERSISTENT = 0;
+ private static final int PREF_RANDOMIZATION_NONE = 1;
+
+ /**
+ * Returns preference index value.
+ *
+ * @param macRandomized is mac randomized value
+ * @return index value of preference
+ */
+ public static int translateMacRandomizedValueToPrefValue(int macRandomized) {
+ return (macRandomized == WifiConfiguration.RANDOMIZATION_PERSISTENT)
+ ? PREF_RANDOMIZATION_PERSISTENT : PREF_RANDOMIZATION_NONE;
+ }
+
+ /**
+ * Returns mac randomized value.
+ *
+ * @param prefMacRandomized is preference index value
+ * @return mac randomized value
+ */
+ public static int translatePrefValueToMacRandomizedValue(int prefMacRandomized) {
+ return (prefMacRandomized == PREF_RANDOMIZATION_PERSISTENT)
+ ? WifiConfiguration.RANDOMIZATION_PERSISTENT : WifiConfiguration.RANDOMIZATION_NONE;
+ }
+
+ private void updateSummary(DropDownPreference preference, int macRandomized) {
+ // Translates value here to set RANDOMIZATION_PERSISTENT as first item in UI for better UX.
+ final int prefMacRandomized = translateMacRandomizedValueToPrefValue(macRandomized);
+ preference.setSummary(preference.getEntries()[prefMacRandomized]);
+ }
+
+ @Override
+ public void onSubmit(WifiDialog dialog) {
+ if (dialog.getController() != null) {
+ final WifiConfiguration newConfig = dialog.getController().getConfig();
+ if (newConfig == null || mWifiConfiguration == null) {
+ return;
+ }
+
+ if (newConfig.macRandomizationSetting != mWifiConfiguration.macRandomizationSetting) {
+ mWifiConfiguration = newConfig;
+ onPreferenceChange(mPreference, String.valueOf(newConfig.macRandomizationSetting));
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
index 3975170..8544a53 100644
--- a/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
+++ b/src/com/android/settings/wifi/p2p/WifiP2pSettings.java
@@ -152,7 +152,7 @@
} else {
updateSearchMenu(false);
}
- } else if (WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION.equals(action)) {
+ } else if (WifiP2pManager.ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED.equals(action)) {
if (mWifiP2pManager != null) {
mWifiP2pManager.requestPersistentGroupInfo(mChannel, WifiP2pSettings.this);
}
@@ -339,7 +339,7 @@
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
- mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_PERSISTENT_GROUPS_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiP2pManager.ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED);
final PreferenceScreen preferenceScreen = getPreferenceScreen();
getActivity().registerReceiver(mReceiver, mIntentFilter);
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java
new file mode 100644
index 0000000..3b735fa
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.content.Context;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.wifi.WifiEntryPreference;
+import com.android.wifitrackerlib.WifiEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Controller that manages a PreferenceGroup, which contains a list of saved access points.
+ */
+public class SavedAccessPointsPreferenceController2 extends BasePreferenceController implements
+ Preference.OnPreferenceClickListener {
+
+ private PreferenceGroup mPreferenceGroup;
+ private SavedAccessPointsWifiSettings2 mHost;
+ @VisibleForTesting
+ List<WifiEntry> mWifiEntries = new ArrayList<>();
+
+ public SavedAccessPointsPreferenceController2(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * Set {@link SavedAccessPointsWifiSettings2} for click callback action.
+ */
+ public SavedAccessPointsPreferenceController2 setHost(SavedAccessPointsWifiSettings2 host) {
+ mHost = host;
+ return this;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return mWifiEntries.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceGroup = screen.findPreference(getPreferenceKey());
+ updatePreference();
+ super.displayPreference(screen);
+ }
+
+ void displayPreference(PreferenceScreen screen, List<WifiEntry> wifiEntries) {
+ if (wifiEntries == null || wifiEntries.isEmpty()) {
+ mWifiEntries.clear();
+ } else {
+ mWifiEntries = wifiEntries;
+ }
+
+ displayPreference(screen);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (mHost != null) {
+ mHost.showWifiPage(preference.getKey(), preference.getTitle());
+ }
+ return false;
+ }
+
+ private void updatePreference() {
+ mPreferenceGroup.removeAll();
+ for (WifiEntry wifiEntry : mWifiEntries) {
+ final WifiEntryPreference preference = new WifiEntryPreference(mContext, wifiEntry);
+ preference.setKey(wifiEntry.getKey());
+ preference.setOnPreferenceClickListener(this);
+
+ mPreferenceGroup.addPreference(preference);
+ }
+ }
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java
new file mode 100644
index 0000000..e094051
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkScoreManager;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SimpleClock;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
+import com.android.wifitrackerlib.SavedNetworkTracker;
+
+import java.time.Clock;
+import java.time.ZoneOffset;
+
+/**
+ * UI to manage saved networks/access points.
+ */
+public class SavedAccessPointsWifiSettings2 extends DashboardFragment
+ implements SavedNetworkTracker.SavedNetworkTrackerCallback {
+
+ private static final String TAG = "SavedAccessPoints2";
+
+ // Key of a Bundle to save/restore the selected WifiEntry
+ static final String KEY_KEY = "key_key";
+
+ // Max age of tracked WifiEntries
+ private static final long MAX_SCAN_AGE_MILLIS = 15_000;
+ // Interval between initiating SavedNetworkTracker scans
+ private static final long SCAN_INTERVAL_MILLIS = 10_000;
+
+ private SavedNetworkTracker mSavedNetworkTracker;
+ private HandlerThread mWorkerThread;
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_SAVED_ACCESS_POINTS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.wifi_display_saved_access_points2;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ use(SavedAccessPointsPreferenceController2.class).setHost(this);
+ use(SubscribedAccessPointsPreferenceController2.class).setHost(this);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Context context = getContext();
+ mWorkerThread = new HandlerThread(TAG
+ + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mWorkerThread.start();
+ final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
+ @Override
+ public long millis() {
+ return SystemClock.elapsedRealtime();
+ }
+ };
+ mSavedNetworkTracker = new SavedNetworkTracker(getSettingsLifecycle(), context,
+ context.getSystemService(WifiManager.class),
+ context.getSystemService(ConnectivityManager.class),
+ context.getSystemService(NetworkScoreManager.class),
+ new Handler(Looper.getMainLooper()),
+ mWorkerThread.getThreadHandler(),
+ elapsedRealtimeClock,
+ MAX_SCAN_AGE_MILLIS,
+ SCAN_INTERVAL_MILLIS,
+ this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ onSavedWifiEntriesChanged();
+ onSubscriptionWifiEntriesChanged();
+ }
+
+ @Override
+ public void onDestroy() {
+ mWorkerThread.quit();
+
+ super.onDestroy();
+ }
+
+ /**
+ * Shows {@link WifiNetworkDetailsFragment2} for assigned key of {@link WifiEntry}.
+ */
+ public void showWifiPage(@NonNull String key, CharSequence title) {
+ removeDialog(WifiSettings.WIFI_DIALOG_ID);
+
+ if (TextUtils.isEmpty(key)) {
+ Log.e(TAG, "Not able to show WifiEntry of an empty key");
+ return;
+ }
+
+ final Bundle bundle = new Bundle();
+ bundle.putString(KEY_KEY, key);
+
+ new SubSettingLauncher(getContext())
+ .setTitleText(title)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(bundle)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ }
+
+ @Override
+ public void onWifiStateChanged() {
+ // Do nothing.
+ }
+
+ @Override
+ public void onSavedWifiEntriesChanged() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ use(SavedAccessPointsPreferenceController2.class)
+ .displayPreference(screen, mSavedNetworkTracker.getSavedWifiEntries());
+ }
+
+ @Override
+ public void onSubscriptionWifiEntriesChanged() {
+ final PreferenceScreen screen = getPreferenceScreen();
+ use(SubscribedAccessPointsPreferenceController2.class)
+ .displayPreference(screen, mSavedNetworkTracker.getSubscriptionWifiEntries());
+ }
+}
diff --git a/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java b/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java
new file mode 100644
index 0000000..8898458
--- /dev/null
+++ b/src/com/android/settings/wifi/savedaccesspoints2/SubscribedAccessPointsPreferenceController2.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import android.content.Context;
+
+/**
+ * Controller that manages a PreferenceGroup, which contains a list of subscribed access points.
+ */
+public class SubscribedAccessPointsPreferenceController2 extends
+ SavedAccessPointsPreferenceController2 {
+
+ public SubscribedAccessPointsPreferenceController2(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+}
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index 97b9241..6b9dd3c 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -21,7 +21,6 @@
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiSsid;
import android.text.TextUtils;
import android.util.Log;
@@ -72,12 +71,12 @@
}
private boolean hasWorkingNetwork() {
- return !TextUtils.equals(getActiveSSID(), WifiSsid.NONE) && hasInternetAccess();
+ return !TextUtils.equals(getActiveSSID(), WifiManager.UNKNOWN_SSID) && hasInternetAccess();
}
private String getActiveSSID() {
if (mWifiManager.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
- return WifiSsid.NONE;
+ return WifiManager.UNKNOWN_SSID;
}
return WifiInfo.removeDoubleQuotes(mWifiManager.getConnectionInfo().getSSID());
}
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index e5c8de5..97ac9c5 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -38,6 +38,7 @@
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
@@ -54,8 +55,10 @@
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiDialogActivity;
import com.android.settings.wifi.WifiSettings;
+import com.android.settings.wifi.WifiSettings2;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
+import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.settingslib.wifi.AccessPoint;
import java.util.Arrays;
@@ -247,13 +250,24 @@
accessPoint.saveWifiState(extras);
if (accessPoint.isActive()) {
- final Intent intent = new SubSettingLauncher(mContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(extras)
- .setSourceMetricsCategory(SettingsEnums.WIFI)
- .toIntent();
- return getActivityAction(requestCode, intent, icon, title);
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = new SubSettingLauncher(mContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment2.class.getName())
+ .setArguments(extras)
+ .setSourceMetricsCategory(SettingsEnums.WIFI)
+ .toIntent();
+ return getActivityAction(requestCode, intent, icon, title);
+ } else {
+ intent = new SubSettingLauncher(mContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setDestination(WifiNetworkDetailsFragment.class.getName())
+ .setArguments(extras)
+ .setSourceMetricsCategory(SettingsEnums.WIFI)
+ .toIntent();
+ return getActivityAction(requestCode, intent, icon, title);
+ }
} else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
@@ -317,11 +331,21 @@
public Intent getIntent() {
final String screenTitle = mContext.getText(R.string.wifi_settings).toString();
final Uri contentUri = new Uri.Builder().appendPath(KEY_WIFI).build();
- final Intent intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+
+ Intent intent;
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
+ WifiSettings2.class.getName(), KEY_WIFI, screenTitle,
+ SettingsEnums.DIALOG_WIFI_AP_EDIT)
+ .setClassName(mContext.getPackageName(), SubSettings.class.getName())
+ .setData(contentUri);
+ } else {
+ intent = SliceBuilderUtils.buildSearchResultPageIntent(mContext,
WifiSettings.class.getName(), KEY_WIFI, screenTitle,
SettingsEnums.DIALOG_WIFI_AP_EDIT)
.setClassName(mContext.getPackageName(), SubSettings.class.getName())
.setData(contentUri);
+ }
return intent;
}
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
index 96372be..653f074 100644
--- a/tests/robotests/Android.bp
+++ b/tests/robotests/Android.bp
@@ -62,6 +62,7 @@
static_libs: [
"SettingsLib-robo-testutils",
+ "app-compat-annotations",
],
java_resource_dirs: ["config", "resources"],
diff --git a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
index 675108d..c29807b 100644
--- a/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
+++ b/tests/robotests/assets/grandfather_invalid_base_preference_controller_constructor
@@ -11,4 +11,5 @@
com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController
com.android.settings.security.VisiblePatternProfilePreferenceController
-com.android.settings.wifi.details.WifiMeteredPreferenceController
\ No newline at end of file
+com.android.settings.wifi.details.WifiMeteredPreferenceController
+com.android.settings.wifi.details2.WifiMeteredPreferenceController2
diff --git a/tests/robotests/assets/grandfather_not_implementing_index_provider b/tests/robotests/assets/grandfather_not_implementing_index_provider
index 6ef0018..b69d72e 100644
--- a/tests/robotests/assets/grandfather_not_implementing_index_provider
+++ b/tests/robotests/assets/grandfather_not_implementing_index_provider
@@ -29,6 +29,9 @@
com.android.settings.bluetooth.BluetoothDeviceDetailsFragment
com.android.settings.bluetooth.BluetoothPairingDetail
com.android.settings.bluetooth.DevicePickerFragment
+com.android.settings.dashboard.profileselector.ProfileSelectAccountFragment
+com.android.settings.dashboard.profileselector.ProfileSelectManageApplications
+com.android.settings.dashboard.profileselector.ProfileSelectStorageFragment
com.android.settings.datausage.AppDataUsage
com.android.settings.datausage.DataUsageList
com.android.settings.datausage.DataUsageSummary
@@ -87,4 +90,6 @@
com.android.settings.wifi.p2p.WifiP2pSettings
com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings
com.android.settings.wifi.WifiInfo
-com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails
\ No newline at end of file
+com.android.settings.applications.specialaccess.notificationaccess.NotificationAccessDetails
+com.android.settings.wifi.details2.WifiNetworkDetailsFragment2
+com.android.settings.wifi.savedaccesspoints2.SavedAccessPointsWifiSettings2
diff --git a/tests/robotests/res/values-mcc999/config.xml b/tests/robotests/res/values-mcc999/config.xml
index 0dec65f..679062b 100644
--- a/tests/robotests/res/values-mcc999/config.xml
+++ b/tests/robotests/res/values-mcc999/config.xml
@@ -92,5 +92,6 @@
<!-- List containing the injected tile keys which are suppressed. -->
<string-array name="config_suppress_injected_tile_keys" translatable="false">
<item>injected_tile_key</item>
+ <item>injected_tile_key2</item>
</string-array>
</resources>
diff --git a/tests/robotests/src/com/android/settings/SettingsActivityTest.java b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
index 7906803..d2b4733 100644
--- a/tests/robotests/src/com/android/settings/SettingsActivityTest.java
+++ b/tests/robotests/src/com/android/settings/SettingsActivityTest.java
@@ -30,11 +30,13 @@
import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
+import android.util.FeatureFlagUtils;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import com.android.settings.core.FeatureFlags;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -65,6 +67,8 @@
mContext = RuntimeEnvironment.application;
mActivity = spy(new SettingsActivity());
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.PERSONAL_WORK_PROFILE, false);
+
}
@Test
diff --git a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
index 03c5de3..a827284 100644
--- a/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accounts/AccountDetailDashboardFragmentTest.java
@@ -151,9 +151,9 @@
final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
final Preference preference = new Preference(mContext);
- dashboardFeatureProvider.bindPreferenceToTile(activity, false /* forceRoundedIcon */,
- MetricsProto.MetricsEvent.DASHBOARD_SUMMARY, preference, tile, null /* key */,
- Preference.DEFAULT_ORDER);
+ dashboardFeatureProvider.bindPreferenceToTileAndGetObservers(activity,
+ false /* forceRoundedIcon */, MetricsProto.MetricsEvent.DASHBOARD_SUMMARY,
+ preference, tile, null /* key */, Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
preference.performClick();
diff --git a/tests/robotests/src/com/android/settings/dashboard/CategoryManagerTest.java b/tests/robotests/src/com/android/settings/dashboard/CategoryManagerTest.java
index 1efaed9..64eaad5 100644
--- a/tests/robotests/src/com/android/settings/dashboard/CategoryManagerTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/CategoryManagerTest.java
@@ -18,18 +18,21 @@
import static com.android.settingslib.drawer.CategoryKey.CATEGORY_HOMEPAGE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.ProviderInfo;
import android.os.Bundle;
import android.util.Pair;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.DashboardCategory;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import org.junit.Before;
@@ -132,25 +135,9 @@
// Create some fake tiles that are not sorted.
final String testPackage = "com.android.test";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
- activityInfo1.packageName = testPackage;
- activityInfo1.name = "class1";
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 50);
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class2";
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 200);
- activityInfo3.packageName = testPackage;
- activityInfo3.name = "class3";
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
-
+ final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 50);
+ final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 200);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -171,25 +158,9 @@
final String testPackage1 = "com.android.test1";
final String testPackage2 = "com.android.test2";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
- activityInfo1.packageName = testPackage2;
- activityInfo1.name = "class1";
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
- activityInfo2.packageName = testPackage1;
- activityInfo2.name = "class2";
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
- activityInfo3.packageName = testPackage1;
- activityInfo3.name = "class3";
-
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage2, "class1", 100);
+ final Tile tile2 = createActivityTile(category.key, testPackage1, "class2", 100);
+ final Tile tile3 = createActivityTile(category.key, testPackage1, "class3", 50);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -209,24 +180,9 @@
// Create some fake tiles that are not sorted.
final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.packageName = testPackage;
- activityInfo1.name = "class1";
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class2";
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.packageName = testPackage;
- activityInfo3.name = "class3";
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 100);
+ final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 50);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -247,32 +203,10 @@
final String testPackage = mContext.getPackageName();
final String testPackage2 = "com.google.test2";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
-
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.packageName = testPackage;
- activityInfo1.name = "class1";
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 2);
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class2";
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 1);
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.packageName = testPackage2;
- activityInfo3.name = "class0";
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 0);
- final ActivityInfo activityInfo4 = new ActivityInfo();
- activityInfo4.packageName = testPackage;
- activityInfo4.name = "class3";
- activityInfo4.metaData = new Bundle();
- activityInfo4.metaData.putInt(META_DATA_KEY_ORDER, -1);
-
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile4 = new ActivityTile(activityInfo4, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 2);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 1);
+ final Tile tile3 = createActivityTile(category.key, testPackage2, "class0", 0);
+ final Tile tile4 = createActivityTile(category.key, testPackage, "class3", -1);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -296,24 +230,9 @@
final String testPackage2 = "com.google.test2";
final String testPackage3 = "com.abcde.test3";
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.packageName = testPackage2;
- activityInfo1.name = "class1";
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 1);
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class2";
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 1);
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.packageName = testPackage3;
- activityInfo3.name = "class3";
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 1);
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage2, "class1", 1);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 1);
+ final Tile tile3 = createActivityTile(category.key, testPackage3, "class3", 1);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -333,58 +252,36 @@
// Create some unique tiles
final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.packageName = testPackage;
- activityInfo1.name = "class1";
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class2";
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.packageName = testPackage;
- activityInfo3.name = "class3";
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class2", 100);
+ final Tile tile3 = createActivityTile(category.key, testPackage, "class3", 50);
+ final Tile tile4 = createProviderTile(category.key, testPackage, "class1", "authority1",
+ "key1", 100);
+ final Tile tile5 = createProviderTile(category.key, testPackage, "class1", "authority2",
+ "key2", 100);
+ final Tile tile6 = createProviderTile(category.key, testPackage, "class1", "authority1",
+ "key2", 50);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
+ category.addTile(tile4);
+ category.addTile(tile5);
+ category.addTile(tile6);
mCategoryByKeyMap.put(CATEGORY_HOMEPAGE, category);
mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
- assertThat(category.getTilesCount()).isEqualTo(3);
+ assertThat(category.getTilesCount()).isEqualTo(6);
}
@Test
- public void filterTiles_hasDuplicate_shouldOnlyKeepUniqueTiles() {
+ public void filterTiles_hasDuplicateActivityTiles_shouldOnlyKeepUniqueTiles() {
// Create tiles pointing to same intent.
final String testPackage = mContext.getPackageName();
final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
- final ActivityInfo activityInfo1 = new ActivityInfo();
- activityInfo1.packageName = testPackage;
- activityInfo1.name = "class1";
- activityInfo1.metaData = new Bundle();
- activityInfo1.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo2 = new ActivityInfo();
- activityInfo2.packageName = testPackage;
- activityInfo2.name = "class1";
- activityInfo2.metaData = new Bundle();
- activityInfo2.metaData.putInt(META_DATA_KEY_ORDER, 100);
- final ActivityInfo activityInfo3 = new ActivityInfo();
- activityInfo3.packageName = testPackage;
- activityInfo3.name = "class1";
- activityInfo3.metaData = new Bundle();
- activityInfo3.metaData.putInt(META_DATA_KEY_ORDER, 50);
-
- final Tile tile1 = new ActivityTile(activityInfo1, category.key);
- final Tile tile2 = new ActivityTile(activityInfo2, category.key);
- final Tile tile3 = new ActivityTile(activityInfo3, category.key);
+ final Tile tile1 = createActivityTile(category.key, testPackage, "class1", 100);
+ final Tile tile2 = createActivityTile(category.key, testPackage, "class1", 100);
+ final Tile tile3 = createActivityTile(category.key, testPackage, "class1", 50);
category.addTile(tile1);
category.addTile(tile2);
category.addTile(tile3);
@@ -394,4 +291,47 @@
assertThat(category.getTilesCount()).isEqualTo(1);
}
+
+ @Test
+ public void filterTiles_hasDuplicateProviderTiles_shouldOnlyKeepUniqueTiles() {
+ // Create tiles pointing to same authority and key.
+ final String testPackage = mContext.getPackageName();
+ final DashboardCategory category = new DashboardCategory(CATEGORY_HOMEPAGE);
+ final Tile tile1 = createProviderTile(category.key, testPackage, "class1", "authority1",
+ "key1", 100);
+ final Tile tile2 = createProviderTile(category.key, testPackage, "class2", "authority1",
+ "key1", 100);
+ final Tile tile3 = createProviderTile(category.key, testPackage, "class3", "authority1",
+ "key1", 50);
+ category.addTile(tile1);
+ category.addTile(tile2);
+ category.addTile(tile3);
+ mCategoryByKeyMap.put(CATEGORY_HOMEPAGE, category);
+
+ mCategoryManager.filterDuplicateTiles(mCategoryByKeyMap);
+
+ assertThat(category.getTilesCount()).isEqualTo(1);
+ }
+
+ private Tile createActivityTile(String categoryKey, String packageName, String className,
+ int order) {
+ final ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = packageName;
+ activityInfo.name = className;
+ activityInfo.metaData = new Bundle();
+ activityInfo.metaData.putInt(META_DATA_KEY_ORDER, order);
+ return new ActivityTile(activityInfo, categoryKey);
+ }
+
+ private Tile createProviderTile(String categoryKey, String packageName, String className,
+ String authority, String key, int order) {
+ final ProviderInfo providerInfo = new ProviderInfo();
+ final Bundle metaData = new Bundle();
+ providerInfo.packageName = packageName;
+ providerInfo.name = className;
+ providerInfo.authority = authority;
+ metaData.putString(META_DATA_PREFERENCE_KEYHINT, key);
+ metaData.putInt(META_DATA_KEY_ORDER, order);
+ return new ProviderTile(providerInfo, categoryKey, metaData);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
index 7dafd07..14fd284 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFeatureProviderImplTest.java
@@ -18,10 +18,12 @@
import static android.content.Intent.EXTRA_USER;
+import static com.android.settingslib.drawer.SwitchesProvider.EXTRA_SWITCH_SET_CHECKED_ERROR;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_ORDER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_KEY_PROFILE;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SUMMARY;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_TITLE;
import static com.android.settingslib.drawer.TileUtils.PROFILE_ALL;
import static com.android.settingslib.drawer.TileUtils.PROFILE_PRIMARY;
@@ -43,6 +45,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
@@ -52,6 +55,7 @@
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
@@ -62,6 +66,7 @@
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
+import com.android.settingslib.drawer.ProviderTile;
import com.android.settingslib.drawer.Tile;
import com.android.settingslib.drawer.TileUtils;
@@ -81,11 +86,15 @@
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
+import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowUserManager.class)
public class DashboardFeatureProviderImplTest {
+ private static final String KEY = "key";
+ private static final String SWITCH_URI = "content://com.android.settings/tile_switch";
+
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private FragmentActivity mActivity;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -96,6 +105,8 @@
private Context mContext;
private ActivityInfo mActivityInfo;
+ private ProviderInfo mProviderInfo;
+ private Bundle mSwitchMetaData;
private DashboardFeatureProviderImpl mImpl;
private boolean mForceRoundedIcon;
@@ -112,6 +123,17 @@
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
mActivityInfo.metaData.putInt(META_DATA_PREFERENCE_SUMMARY,
R.string.about_settings_summary);
+
+ mProviderInfo = new ProviderInfo();
+ mProviderInfo.packageName = mContext.getPackageName();
+ mProviderInfo.name = "provider";
+ mProviderInfo.authority = "com.android.settings";
+ mSwitchMetaData = new Bundle();
+ mSwitchMetaData.putInt(META_DATA_PREFERENCE_TITLE, R.string.settings_label);
+ mSwitchMetaData.putInt(META_DATA_PREFERENCE_SUMMARY, R.string.about_settings_summary);
+ mSwitchMetaData.putString(META_DATA_PREFERENCE_KEYHINT, KEY);
+ mSwitchMetaData.putString(META_DATA_PREFERENCE_SWITCH_URI, SWITCH_URI);
+
doReturn(mPackageManager).when(mContext).getPackageManager();
when(mPackageManager.resolveActivity(any(Intent.class), anyInt()))
.thenReturn(new ResolveInfo());
@@ -132,8 +154,8 @@
doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
.when(tile).getIcon(any(Context.class));
mActivityInfo.metaData.putString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS, "HI");
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
assertThat(preference.getSummary())
@@ -145,13 +167,34 @@
}
@Test
+ public void bindPreference_shouldBindAllSwitchData() {
+ final Preference preference = new SwitchPreference(RuntimeEnvironment.application);
+ final Tile tile = spy(new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
+ mSwitchMetaData));
+ mSwitchMetaData.putInt(META_DATA_KEY_ORDER, 10);
+ doReturn(Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565)))
+ .when(tile).getIcon(any(Context.class));
+ final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
+ mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES, preference, tile,
+ null /* key*/, Preference.DEFAULT_ORDER);
+
+ assertThat(preference.getTitle()).isEqualTo(mContext.getText(R.string.settings_label));
+ assertThat(preference.getSummary())
+ .isEqualTo(mContext.getText(R.string.about_settings_summary));
+ assertThat(preference.getKey()).isEqualTo(KEY);
+ assertThat(preference.getIcon()).isNotNull();
+ assertThat(preference.getOrder()).isEqualTo(tile.getOrder());
+ assertThat(observers.get(0).getUri().toString()).isEqualTo(SWITCH_URI);
+ }
+
+ @Test
public void bindPreference_noFragmentMetadata_shouldBindIntent() {
final Preference preference = new Preference(RuntimeEnvironment.application);
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getFragment()).isNull();
assertThat(preference.getOnPreferenceClickListener()).isNotNull();
@@ -166,8 +209,8 @@
tile.userHandle.add(mock(UserHandle.class));
tile.userHandle.add(mock(UserHandle.class));
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mActivity).getSupportFragmentManager();
@@ -183,8 +226,8 @@
when(mActivity.getSystemService(Context.USER_SERVICE))
.thenReturn(mUserManager);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
@@ -203,8 +246,8 @@
tile.userHandle = new ArrayList<>();
tile.userHandle.add(mock(UserHandle.class));
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.getOnPreferenceClickListener().onPreferenceClick(null);
verify(mFeatureFactory.metricsFeatureProvider).logDashboardStartIntent(
any(Context.class),
@@ -217,8 +260,8 @@
@Test
public void bindPreference_nullPreference_shouldIgnore() {
final Tile tile = mock(Tile.class);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- null, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, null, tile, "123", Preference.DEFAULT_ORDER);
verifyZeroInteractions(tile);
}
@@ -227,8 +270,9 @@
public void bindPreference_withNullKeyNullPriority_shouldGenerateKeyAndPriority() {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, null /*key */, Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
+ Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isNotNull();
assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
@@ -241,8 +285,9 @@
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, null /*key */, Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
+ Preference.DEFAULT_ORDER);
assertThat(preference.getSummary())
.isEqualTo(RuntimeEnvironment.application.getString(R.string.summary_placeholder));
@@ -250,30 +295,99 @@
@Test
@Config(shadows = {ShadowTileUtils.class})
- public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProvider() {
+ public void bindPreference_hasSummaryUri_shouldLoadSummaryFromContentProviderAndHaveObserver() {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI,
- "content://com.android.settings/tile_summary");
+ final String uriString = "content://com.android.settings/tile_summary";
+ mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_SUMMARY_URI, uriString);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, null /*key */, Preference.DEFAULT_ORDER);
+ final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
+ mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
+ null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getSummary()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
+ assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
}
@Test
@Config(shadows = {ShadowTileUtils.class})
- public void bindPreference_hasTitleUri_shouldLoadFromContentProvider() {
+ public void bindPreference_hasTitleUri_shouldLoadFromContentProviderAndHaveObserver() {
final Preference preference = new Preference(RuntimeEnvironment.application);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI,
- "content://com.android.settings/tile_title");
+ final String uriString = "content://com.android.settings/tile_title";
+ mActivityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE_URI, uriString);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, null /*key */, Preference.DEFAULT_ORDER);
+ final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
+ mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
+ null /*key */, Preference.DEFAULT_ORDER);
assertThat(preference.getTitle()).isEqualTo(ShadowTileUtils.MOCK_SUMMARY);
+ assertThat(observers.get(0).getUri().toString()).isEqualTo(uriString);
+ }
+
+ @Test
+ @Config(shadows = {ShadowTileUtils.class})
+ public void bindPreference_onCheckedChanged_shouldPutStateToContentProvider() {
+ final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
+ final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
+ mSwitchMetaData);
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, false);
+ ShadowTileUtils.setResultBundle(bundle);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
+ Preference.DEFAULT_ORDER);
+
+ preference.callChangeListener(false);
+
+ assertThat(ShadowTileUtils.getProviderChecked()).isFalse();
+
+ preference.callChangeListener(true);
+
+ assertThat(ShadowTileUtils.getProviderChecked()).isTrue();
+ }
+
+ @Test
+ @Config(shadows = {ShadowTileUtils.class})
+ public void bindPreference_onCheckedChangedError_shouldRevertCheckedState() {
+ final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
+ final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
+ mSwitchMetaData);
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean(EXTRA_SWITCH_SET_CHECKED_ERROR, true);
+ ShadowTileUtils.setResultBundle(bundle);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, null /*key */,
+ Preference.DEFAULT_ORDER);
+
+ preference.callChangeListener(true);
+
+ assertThat(preference.isChecked()).isFalse();
+
+ preference.callChangeListener(false);
+
+ assertThat(preference.isChecked()).isTrue();
+ }
+
+ @Test
+ @Config(shadows = {ShadowTileUtils.class})
+ public void bindPreference_callbackOnChanged_shouldLoadFromContentProvider() {
+ final SwitchPreference preference = new SwitchPreference(RuntimeEnvironment.application);
+ final Tile tile = new ProviderTile(mProviderInfo, CategoryKey.CATEGORY_HOMEPAGE,
+ mSwitchMetaData);
+ final List<DynamicDataObserver> observers = mImpl.bindPreferenceToTileAndGetObservers(
+ mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN, preference, tile,
+ null /*key */, Preference.DEFAULT_ORDER);
+
+ ShadowTileUtils.setProviderChecked(false);
+ observers.get(0).onDataChanged();
+
+ assertThat(preference.isChecked()).isFalse();
+
+ ShadowTileUtils.setProviderChecked(true);
+ observers.get(0).onDataChanged();
+
+ assertThat(preference.isChecked()).isTrue();
}
@Test
@@ -281,8 +395,9 @@
final Preference preference = new Preference(RuntimeEnvironment.application);
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, null /* key */, Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, null /* key */,
+ Preference.DEFAULT_ORDER);
assertThat(preference.getKey()).isEqualTo(tile.getKey(mContext));
}
@@ -308,8 +423,8 @@
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, "123", baseOrder);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", baseOrder);
assertThat(preference.getOrder()).isEqualTo(tile.getOrder() + baseOrder);
}
@@ -321,8 +436,8 @@
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, 10);
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putInt(META_DATA_KEY_ORDER, testOrder);
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(testOrder);
}
@@ -333,8 +448,8 @@
final Tile tile = new ActivityTile(mActivityInfo, CategoryKey.CATEGORY_HOMEPAGE);
mActivityInfo.metaData.putString(META_DATA_KEY_ORDER, "hello");
- mImpl.bindPreferenceToTile(mActivity, mForceRoundedIcon, MetricsEvent.VIEW_UNKNOWN,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(mActivity, mForceRoundedIcon,
+ MetricsEvent.VIEW_UNKNOWN, preference, tile, "123", Preference.DEFAULT_ORDER);
assertThat(preference.getOrder()).isEqualTo(Preference.DEFAULT_ORDER);
}
@@ -347,8 +462,8 @@
mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "key");
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null;
- mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick();
ShadowActivity shadowActivity = Shadows.shadowOf(activity);
@@ -371,8 +486,8 @@
mActivityInfo.metaData.putString("com.android.settings.intent.action", "TestAction");
tile.userHandle = null;
- mImpl.bindPreferenceToTile(activity, mForceRoundedIcon, MetricsEvent.SETTINGS_GESTURES,
- preference, tile, "123", Preference.DEFAULT_ORDER);
+ mImpl.bindPreferenceToTileAndGetObservers(activity, mForceRoundedIcon,
+ MetricsEvent.SETTINGS_GESTURES, preference, tile, "123", Preference.DEFAULT_ORDER);
preference.performClick();
final ShadowActivity.IntentForResult launchIntent =
diff --git a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
index 954a872..146b37a 100644
--- a/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/dashboard/DashboardFragmentTest.java
@@ -17,9 +17,11 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.DASHBOARD_CONTAINER;
import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_KEYHINT;
+import static com.android.settingslib.drawer.TileUtils.META_DATA_PREFERENCE_SWITCH_URI;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -29,14 +31,18 @@
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.Bundle;
import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.core.PreferenceControllerMixin;
@@ -47,7 +53,7 @@
import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.DashboardCategory;
-import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.drawer.ProviderTile;
import org.junit.Before;
import org.junit.Test;
@@ -57,6 +63,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
@@ -70,24 +78,37 @@
@Mock
private FakeFeatureFactory mFakeFeatureFactory;
- private ActivityInfo mActivityInfo;
private DashboardCategory mDashboardCategory;
private Context mContext;
private TestFragment mTestFragment;
private List<AbstractPreferenceController> mControllers;
+ private ActivityTile mActivityTile;
+ private ProviderTile mProviderTile;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
- mActivityInfo = new ActivityInfo();
- mActivityInfo.packageName = "pkg";
- mActivityInfo.name = "class";
- mActivityInfo.metaData = new Bundle();
- mActivityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key");
+ final ActivityInfo activityInfo = new ActivityInfo();
+ activityInfo.packageName = "pkg";
+ activityInfo.name = "class";
+ activityInfo.metaData = new Bundle();
+ activityInfo.metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key");
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mDashboardCategory = new DashboardCategory("key");
- mDashboardCategory.addTile(new ActivityTile(mActivityInfo, mDashboardCategory.key));
+ mActivityTile = new ActivityTile(activityInfo, mDashboardCategory.key);
+ mDashboardCategory.addTile(mActivityTile);
+
+ final ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.packageName = "pkg";
+ providerInfo.name = "provider";
+ providerInfo.authority = "authority";
+ final Bundle metaData = new Bundle();
+ metaData.putString(META_DATA_PREFERENCE_KEYHINT, "injected_tile_key2");
+ metaData.putString(META_DATA_PREFERENCE_SWITCH_URI, "uri");
+ mProviderTile = new ProviderTile(providerInfo, mDashboardCategory.key, metaData);
+ mDashboardCategory.addTile(mProviderTile);
+
mTestFragment = new TestFragment(RuntimeEnvironment.application);
when(mFakeFeatureFactory.dashboardFeatureProvider
.getTilesForCategory(nullable(String.class)))
@@ -127,11 +148,14 @@
.getTilesForCategory(nullable(String.class)))
.thenReturn(mDashboardCategory);
when(mFakeFeatureFactory.dashboardFeatureProvider
- .getDashboardKeyForTile(nullable(Tile.class)))
+ .getDashboardKeyForTile(any(ActivityTile.class)))
.thenReturn("test_key");
+ when(mFakeFeatureFactory.dashboardFeatureProvider
+ .getDashboardKeyForTile(any(ProviderTile.class)))
+ .thenReturn("test_key2");
mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
- verify(mTestFragment.mScreen).addPreference(nullable(Preference.class));
+ verify(mTestFragment.mScreen, times(2)).addPreference(nullable(Preference.class));
}
@Test
@@ -156,8 +180,11 @@
.getTilesForCategory(nullable(String.class)))
.thenReturn(mDashboardCategory);
when(mFakeFeatureFactory.dashboardFeatureProvider
- .getDashboardKeyForTile(nullable(Tile.class)))
+ .getDashboardKeyForTile(any(ActivityTile.class)))
.thenReturn("test_key");
+ when(mFakeFeatureFactory.dashboardFeatureProvider
+ .getDashboardKeyForTile(any(ProviderTile.class)))
+ .thenReturn("test_key2");
mTestFragment.onCreatePreferences(new Bundle(), "rootKey");
verify(mTestFragment.mScreen, never()).addPreference(nullable(Preference.class));
@@ -172,6 +199,29 @@
}
@Test
+ @Config(shadows = ShadowPreferenceFragmentCompat.class)
+ public void onStart_shouldRegisterDynamicDataObservers() {
+ final DynamicDataObserver observer = new TestDynamicDataObserver();
+ mTestFragment.mDashboardTilePrefKeys.put("key", Arrays.asList(observer));
+
+ mTestFragment.onStart();
+
+ verify(mTestFragment.getContentResolver()).registerContentObserver(observer.getUri(), false,
+ observer);
+ }
+
+ @Test
+ @Config(shadows = ShadowPreferenceFragmentCompat.class)
+ public void onStop_shouldUnregisterDynamicDataObservers() {
+ final DynamicDataObserver observer = new TestDynamicDataObserver();
+ mTestFragment.registerDynamicDataObservers(Arrays.asList(observer));
+
+ mTestFragment.onStop();
+
+ verify(mTestFragment.getContentResolver()).unregisterContentObserver(observer);
+ }
+
+ @Test
public void updateState_skipUnavailablePrefs() {
final List<AbstractPreferenceController> preferenceControllers = mTestFragment.mControllers;
final AbstractPreferenceController mockController1 =
@@ -266,7 +316,14 @@
assertThat(mTestFragment.mBlockerController).isNull();
}
- public static class TestPreferenceController extends AbstractPreferenceController
+ @Test
+ public void createPreference_isProviderTile_returnSwitchPreference() {
+ final Preference pref = mTestFragment.createPreference(mProviderTile);
+
+ assertThat(pref).isInstanceOf(SwitchPreference.class);
+ }
+
+ private static class TestPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin {
private TestPreferenceController(Context context) {
@@ -293,18 +350,20 @@
}
}
- public static class TestFragment extends DashboardFragment {
+ private static class TestFragment extends DashboardFragment {
+
+ public final PreferenceScreen mScreen;
private final PreferenceManager mPreferenceManager;
private final Context mContext;
private final List<AbstractPreferenceController> mControllers;
-
- public final PreferenceScreen mScreen;
+ private final ContentResolver mContentResolver;
public TestFragment(Context context) {
mContext = context;
mPreferenceManager = mock(PreferenceManager.class);
mScreen = mock(PreferenceScreen.class);
+ mContentResolver = mock(ContentResolver.class);
mControllers = new ArrayList<>();
when(mPreferenceManager.getContext()).thenReturn(mContext);
@@ -346,5 +405,36 @@
public PreferenceManager getPreferenceManager() {
return mPreferenceManager;
}
+
+ @Override
+ protected ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+ }
+
+ private static class TestDynamicDataObserver extends DynamicDataObserver {
+
+ @Override
+ public Uri getUri() {
+ return Uri.parse("content://abc");
+ }
+
+ @Override
+ public void onDataChanged() {
+ }
+ }
+
+ @Implements(PreferenceFragmentCompat.class)
+ public static class ShadowPreferenceFragmentCompat {
+
+ @Implementation
+ public void onStart() {
+ // do nothing
+ }
+
+ @Implementation
+ public void onStop() {
+ // do nothing
+ }
}
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
index 4543114..80a3a11 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/BuildNumberPreferenceControllerTest.java
@@ -28,7 +28,9 @@
import android.app.Activity;
import android.content.ClipboardManager;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -120,7 +122,7 @@
@Test
public void handlePrefTreeClick_notAdminUser_isDemoUser_handleBuildNumberPref() {
mShadowUserManager.setIsAdminUser(false);
- mShadowUserManager.setIsDemoUser(true);
+ mShadowUserManager.addUser(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO);
assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue();
}
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
index 0b56cda..65dc621 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/SecondaryUserControllerTest.java
@@ -29,12 +29,14 @@
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.UserManager;
+import android.util.FeatureFlagUtils;
import android.util.SparseArray;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
+import com.android.settings.core.FeatureFlags;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.drawable.UserIconDrawable;
@@ -78,6 +80,8 @@
when(mScreen.getContext()).thenReturn(mContext);
when(mScreen.findPreference(anyString())).thenReturn(mGroup);
when(mGroup.getKey()).thenReturn(TARGET_PREFERENCE_GROUP_KEY);
+
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.PERSONAL_WORK_PROFILE, false);
}
@Test
@@ -119,7 +123,8 @@
}
@Test
- public void secondaryUserAddedIfHasDistinctId() {
+ public void getSecondaryUserControllers_notWorkProfile_addSecondaryUserController() {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.PERSONAL_WORK_PROFILE, false);
final ArrayList<UserInfo> userInfos = new ArrayList<>();
final UserInfo secondaryUser = new UserInfo();
secondaryUser.id = 10;
@@ -136,6 +141,25 @@
}
@Test
+ public void getSecondaryUserControllers_workProfile_addNoSecondaryUserController() {
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlags.PERSONAL_WORK_PROFILE, true);
+ final ArrayList<UserInfo> userInfos = new ArrayList<>();
+ final UserInfo secondaryUser = new UserInfo();
+ secondaryUser.id = 10;
+ secondaryUser.profileGroupId = 101010; // this just has to be something not 0
+ userInfos.add(mPrimaryUser);
+ userInfos.add(secondaryUser);
+ when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
+ when(mUserManager.getUsers()).thenReturn(userInfos);
+ final List<AbstractPreferenceController> controllers =
+ SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
+
+ assertThat(controllers).hasSize(1);
+ assertThat(controllers.get(
+ 0) instanceof SecondaryUserController.NoSecondaryUserController).isTrue();
+ }
+
+ @Test
public void profilesOfPrimaryUserAreNotIgnored() {
final ArrayList<UserInfo> userInfos = new ArrayList<>();
final UserInfo secondaryUser = new UserInfo();
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
index 73fea77..32c706c 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceControllerTest.java
@@ -16,15 +16,13 @@
package com.android.settings.deviceinfo.storage;
import static com.android.settings.applications.manageapplications.ManageApplications.EXTRA_WORK_ID;
-import static com.android.settings.applications.manageapplications.ManageApplications
- .EXTRA_WORK_ONLY;
import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -51,6 +49,7 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.applications.manageapplications.ManageApplications;
+import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.PrivateVolumeSettings;
import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -195,8 +194,8 @@
.isEqualTo(R.string.apps_storage);
assertThat(
intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
- .getBoolean(EXTRA_WORK_ONLY))
- .isTrue();
+ .getInt(ProfileSelectFragment.EXTRA_PROFILE))
+ .isEqualTo(ProfileSelectFragment.WORK);
assertThat(
intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)
.getInt(EXTRA_WORK_ID))
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
index 4f72318..cd6f082 100644
--- a/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/deviceinfo/storage/StorageSummaryDonutPreferenceControllerTest.java
@@ -37,24 +37,30 @@
import android.widget.Button;
import android.widget.LinearLayout;
+import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.ShadowPrivateStorageInfo;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
import com.android.settingslib.deviceinfo.StorageVolumeProvider;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.io.File;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowPrivateStorageInfo.class)
public class StorageSummaryDonutPreferenceControllerTest {
private Context mContext;
@@ -63,14 +69,18 @@
private PreferenceViewHolder mHolder;
private FakeFeatureFactory mFakeFeatureFactory;
private MetricsFeatureProvider mMetricsFeatureProvider;
+ private PreferenceScreen mScreen;
@Before
public void setUp() throws Exception {
+ ShadowPrivateStorageInfo.setPrivateStorageInfo(new PrivateStorageInfo(10L, 100L));
mContext = spy(Robolectric.setupActivity(Activity.class));
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
- mController = new StorageSummaryDonutPreferenceController(mContext);
+ mController = new StorageSummaryDonutPreferenceController(mContext, "key");
mPreference = new StorageSummaryDonutPreference(mContext);
+ mScreen = spy(new PreferenceScreen(mContext, null));
+ when(mScreen.findPreference("key")).thenReturn(mPreference);
LayoutInflater inflater = LayoutInflater.from(mContext);
final View view =
@@ -79,10 +89,16 @@
mHolder = PreferenceViewHolder.createInstanceForTests(view);
}
+ @After
+ public void tearDown() {
+ ShadowPrivateStorageInfo.reset();
+ }
+
@Test
public void testEmpty() {
final long totalSpace = 32 * GIGABYTE;
final long usedSpace = 0;
+ mController.displayPreference(mScreen);
mController.updateBytes(0, 32 * GIGABYTE);
mController.updateState(mPreference);
@@ -98,6 +114,7 @@
public void testTotalStorage() {
final long totalSpace = KILOBYTE * 10;
final long usedSpace = KILOBYTE;
+ mController.displayPreference(mScreen);
mController.updateBytes(KILOBYTE, totalSpace);
mController.updateState(mPreference);
@@ -121,6 +138,7 @@
when(file.getTotalSpace()).thenReturn(totalSpace);
when(file.getFreeSpace()).thenReturn(freeSpace);
when(svp.getPrimaryStorageSize()).thenReturn(totalSpace);
+ mController.displayPreference(mScreen);
mController.updateSizes(svp, volume);
mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java
index 3659803..9924cb4 100644
--- a/tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/DarkUIPreferenceControllerTest.java
@@ -16,25 +16,9 @@
package com.android.settings.display;
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
import android.content.Context;
-import android.os.Handler;
-import android.os.IPowerManager;
-import android.os.PowerManager;
-
import androidx.fragment.app.Fragment;
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreference;
-
-import org.junit.Assert;
+import com.android.settings.display.darkmode.DarkModePreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,6 +27,10 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
@RunWith(RobolectricTestRunner.class)
public class DarkUIPreferenceControllerTest {
@@ -57,7 +45,7 @@
mContext = spy(RuntimeEnvironment.application);
mController = spy(new DarkUIPreferenceController(mContext, "dark_ui_mode"));
mController.setParentFragment(mFragment);
- mController.mPreference = new SwitchPreference(mContext);
+ mController.mPreference = new DarkModePreference(mContext, null /* AttributeSet attrs */);
mController.onStart();
}
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
new file mode 100644
index 0000000..24cbd07
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.view.View;
+import android.widget.Button;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
+import com.android.settingslib.widget.LayoutPreference;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.eq;
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkModeActivationPreferenceControllerTest {
+ private DarkModeActivationPreferenceController mController;
+ private String mPreferenceKey = "key";
+ @Mock
+ private LayoutPreference mPreference;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Resources res;
+ @Mock
+ private Context mContext;
+ @Mock
+ private UiModeManager mService;
+ @Mock
+ private Button mTurnOffButton;
+ @Mock
+ private Button mTurnOnButton;
+
+ private Configuration configNightYes = new Configuration();
+ private Configuration configNightNo = new Configuration();;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mService = mock(UiModeManager.class);
+ when(mContext.getResources()).thenReturn(res);
+ when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
+ when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+ when(mPreference.findViewById(
+ eq(R.id.dark_ui_turn_on_button))).thenReturn(mTurnOnButton);
+ when(mPreference.findViewById(
+ eq(R.id.dark_ui_turn_off_button))).thenReturn(mTurnOffButton);
+ when(mService.setNightModeActivated(anyBoolean())).thenReturn(true);
+ when(mContext.getString(
+ R.string.dark_ui_activation_off_auto)).thenReturn("off_auto");
+ when(mContext.getString(
+ R.string.dark_ui_activation_on_auto)).thenReturn("on_auto");
+ when(mContext.getString(
+ R.string.dark_ui_activation_off_manual)).thenReturn("off_manual");
+ when(mContext.getString(
+ R.string.dark_ui_activation_on_manual)).thenReturn("on_manual");
+ when(mContext.getString(
+ R.string.dark_ui_summary_off_auto_mode_auto)).thenReturn("summary_off_auto");
+ when(mContext.getString(
+ R.string.dark_ui_summary_on_auto_mode_auto)).thenReturn("summary_on_auto");
+ when(mContext.getString(
+ R.string.dark_ui_summary_off_auto_mode_never)).thenReturn("summary_off_manual");
+ when(mContext.getString(
+ R.string.dark_ui_summary_on_auto_mode_never)).thenReturn("summary_on_manual");
+ mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey);
+ mController.displayPreference(mScreen);
+ configNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
+ configNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
+ }
+
+ @Test
+ public void nightMode_toggleButton_offManual() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+ when(res.getConfiguration()).thenReturn(configNightYes);
+
+ mController.updateState(mPreference);
+
+ verify(mTurnOnButton).setVisibility(eq(View.GONE));
+ verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
+ verify(mTurnOffButton).setText(eq(mContext.getString(
+ R.string.dark_ui_activation_off_manual)));
+ }
+
+
+ @Test
+ public void nightMode_toggleButton_onAutoWhenModeIsYes() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+ when(res.getConfiguration()).thenReturn(configNightNo);
+
+ mController.updateState(mPreference);
+
+ verify(mTurnOffButton).setVisibility(eq(View.GONE));
+ verify(mTurnOnButton).setVisibility(eq(View.VISIBLE));
+ verify(mTurnOnButton).setText(eq(mContext.getString(
+ R.string.dark_ui_activation_on_manual)));
+ }
+
+ @Test
+ public void nightMode_toggleButton_onAutoWhenModeIsAuto() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
+ when(res.getConfiguration()).thenReturn(configNightNo);
+
+ mController.updateState(mPreference);
+
+ verify(mTurnOffButton).setVisibility(eq(View.GONE));
+ verify(mTurnOnButton).setVisibility(eq(View.VISIBLE));
+ verify(mTurnOnButton).setText(eq(mContext.getString(
+ R.string.dark_ui_activation_on_auto)));
+ }
+
+ @Test
+ public void nightModeSummary_buttonText_onManual() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO);
+ when(res.getConfiguration()).thenReturn(configNightYes);
+
+ assertEquals(mController.getSummary(), mContext.getString(
+ R.string.dark_ui_summary_on_auto_mode_never));
+ }
+
+ @Test
+ public void nightModeSummary_buttonText_offAuto() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
+ when(res.getConfiguration()).thenReturn(configNightNo);
+
+ assertEquals(mController.getSummary(), mContext.getString(
+ R.string.dark_ui_summary_off_auto_mode_auto));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeObserverTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeObserverTest.java
new file mode 100644
index 0000000..dfa8ba1
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeObserverTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkModeObserverTest {
+ private Context mContext;
+ private ContentObserver mContentObserver;
+ private DarkModeObserver mDarkModeObserver;
+ private Runnable mCallback;
+ private Uri mUri = Settings.Secure.getUriFor(Settings.Secure.UI_NIGHT_MODE);
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mDarkModeObserver = new DarkModeObserver(mContext);
+ mContentObserver = mDarkModeObserver.getContentObserver();
+ mCallback = mock(Runnable.class);
+ }
+
+ @Test
+ public void callbackTest_subscribedCallbackCalled() {
+ mDarkModeObserver.subscribe(mCallback);
+ mContentObserver.onChange(false, mUri);
+ Mockito.verify(mCallback, times(2)).run();
+ }
+
+ @Test
+ public void callbackTest_unsubscribedCallNotbackCalled() {
+ mDarkModeObserver.subscribe(mCallback);
+ mContentObserver.onChange(false, mUri);
+ mDarkModeObserver.unsubscribe();
+ mContentObserver.onChange(false, mUri);
+ Mockito.verify(mCallback, times(2)).run();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
new file mode 100644
index 0000000..a844fec
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.settings.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import androidx.preference.DropDownPreference;
+import androidx.preference.PreferenceScreen;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import com.android.settings.R;
+
+import static junit.framework.TestCase.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkModeScheduleSelectorControllerTest {
+ private DarkModeScheduleSelectorController mController;
+ private String mPreferenceKey = "key";
+ @Mock
+ private DropDownPreference mPreference;
+ @Mock
+ private PreferenceScreen mScreen;
+ private Context mContext;
+ @Mock
+ private UiModeManager mService;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
+ when(mContext.getString(R.string.dark_ui_auto_mode_never)).thenReturn("never");
+ when(mContext.getString(R.string.dark_ui_auto_mode_auto)).thenReturn("auto");
+ mPreference = spy(new DropDownPreference(mContext));
+ when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+ when(mService.setNightModeActivated(anyBoolean())).thenReturn(true);
+ mController = new DarkModeScheduleSelectorController(mContext, mPreferenceKey);
+ }
+
+ @Test
+ public void nightMode_preferenceChange_preferenceChangeTrueWhenChangedOnly() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+ mController.displayPreference(mScreen);
+ boolean changed = mController
+ .onPreferenceChange(mScreen, mContext.getString(R.string.dark_ui_auto_mode_auto));
+ assertTrue(changed);
+ changed = mController
+ .onPreferenceChange(mScreen, mContext.getString(R.string.dark_ui_auto_mode_auto));
+ assertFalse(changed);
+ }
+
+ @Test
+ public void nightMode_updateStateNone_dropDownValueChangedToNone() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+ mController.displayPreference(mScreen);
+ mController.updateState(mPreference);
+ verify(mPreference).setValue(mContext.getString(R.string.dark_ui_auto_mode_never));
+ }
+
+ @Test
+ public void nightMode_updateStateNone_dropDownValueChangedToAuto() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
+ mController.displayPreference(mScreen);
+ mController.updateState(mPreference);
+ verify(mPreference).setValue(mContext.getString(R.string.dark_ui_auto_mode_auto));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
index dcfba42..8fad638 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSliceTest.java
@@ -81,13 +81,13 @@
}
@Test
- public void updateBatteryTipAvailabilityCache_hasImportantTip_shouldReturnTrue() {
+ public void refreshBatteryTips_hasImportantTip_shouldReturnTrue() {
final List<BatteryTip> tips = new ArrayList<>();
tips.add(new LowBatteryTip(BatteryTip.StateType.INVISIBLE, false, ""));
tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false));
ShadowBatteryTipLoader.setBatteryTips(tips);
- BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
+ BatteryFixSlice.refreshBatteryTips(mContext);
assertThat(BatteryFixSlice.isBatteryTipAvailableFromCache(mContext)).isTrue();
}
@@ -99,7 +99,7 @@
tips.add(new EarlyWarningTip(BatteryTip.StateType.HANDLED, false));
ShadowBatteryTipLoader.setBatteryTips(tips);
- BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
+ BatteryFixSlice.refreshBatteryTips(mContext);
final Slice slice = mSlice.getSlice();
assertThat(SliceMetadata.from(mContext, slice).isErrorSlice()).isTrue();
@@ -115,7 +115,7 @@
tips.add(new EarlyWarningTip(BatteryTip.StateType.NEW, false));
// Create fake cache data
ShadowBatteryTipLoader.setBatteryTips(tips);
- BatteryFixSlice.updateBatteryTipAvailabilityCache(mContext);
+ BatteryFixSlice.refreshBatteryTips(mContext);
// Create fake background worker data
BatteryFixSlice.BatteryTipWorker batteryTipWorker = mock(
BatteryFixSlice.BatteryTipWorker.class);
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java
new file mode 100644
index 0000000..bb21332
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/DarkThemeSliceTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.homepage.contextualcards.slices;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.Context;
+import android.net.Uri;
+import android.os.BatteryManager;
+
+import androidx.slice.Slice;
+import androidx.slice.SliceMetadata;
+import androidx.slice.SliceProvider;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.settings.R;
+import com.android.settings.slices.CustomSliceRegistry;
+import com.android.settings.slices.SlicesFeatureProviderImpl;
+import com.android.settings.testutils.FakeFeatureFactory;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkThemeSliceTest {
+ @Mock
+ private UiModeManager mUiModeManager;
+ @Mock
+ private BatteryManager mBatteryManager;
+
+ private Context mContext;
+ private DarkThemeSlice mDarkThemeSlice;
+ private FakeFeatureFactory mFeatureFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mFeatureFactory.slicesFeatureProvider = new SlicesFeatureProviderImpl();
+ mFeatureFactory.slicesFeatureProvider.newUiSession();
+ doReturn(mUiModeManager).when(mContext).getSystemService(UiModeManager.class);
+
+ // Set-up specs for SliceMetadata.
+ SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+ mDarkThemeSlice = new DarkThemeSlice(mContext);
+ mDarkThemeSlice.sKeepSliceShow = false;
+ }
+
+ @Test
+ public void getUri_shouldBeDarkThemeSliceUri() {
+ final Uri uri = mDarkThemeSlice.getUri();
+
+ assertThat(uri).isEqualTo(CustomSliceRegistry.DARK_THEME_SLICE_URI);
+ }
+
+ @Test
+ public void isAvailable_inDarkThemeMode_returnFalse() {
+ when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+
+ assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse();
+ }
+
+ @Test
+ public void isAvailable_nonDarkThemeBatteryCapacityEq100_returnFalse() {
+ setBatteryCapacityLevel(100);
+
+ assertThat(mDarkThemeSlice.isAvailable(mContext)).isFalse();
+ }
+
+ @Test
+ public void isAvailable_nonDarkThemeBatteryCapacityLt50_returnTrue() {
+ setBatteryCapacityLevel(40);
+
+ assertThat(mDarkThemeSlice.isAvailable(mContext)).isTrue();
+ }
+
+ @Test
+ public void getSlice_notAvailable_returnNull() {
+ when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+
+ assertThat(mDarkThemeSlice.getSlice()).isNull();
+ }
+
+ @Test
+ public void getSlice_newSession_notAvailable_returnNull() {
+ // previous displayed: yes
+ mDarkThemeSlice.sKeepSliceShow = true;
+ // Session: use original value + 1 to become a new session
+ mDarkThemeSlice.sActiveUiSession =
+ mFeatureFactory.slicesFeatureProvider.getUiSessionToken() + 1;
+
+ when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+
+ assertThat(mDarkThemeSlice.getSlice()).isNull();
+ }
+
+ @Test
+ public void getSlice_previouslyDisplayed_isAvailable_returnSlice() {
+ mDarkThemeSlice.sActiveUiSession =
+ mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mDarkThemeSlice.sKeepSliceShow = true;
+ setBatteryCapacityLevel(40);
+
+ assertThat(mDarkThemeSlice.getSlice()).isNotNull();
+ }
+
+ @Test
+ public void getSlice_isAvailable_returnSlice() {
+ setBatteryCapacityLevel(40);
+
+ assertThat(mDarkThemeSlice.getSlice()).isNotNull();
+ }
+
+ @Test
+ public void getSlice_isAvailable_showTitleSubtitle() {
+ setBatteryCapacityLevel(40);
+
+ final Slice slice = mDarkThemeSlice.getSlice();
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ assertThat(metadata.getTitle()).isEqualTo(
+ mContext.getString(R.string.dark_theme_slice_title));
+ assertThat(metadata.getSubtitle()).isEqualTo(
+ mContext.getString(R.string.dark_theme_slice_subtitle));
+ }
+
+ private void setBatteryCapacityLevel(int power_level) {
+ when(mUiModeManager.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO);
+ doReturn(mBatteryManager).when(mContext).getSystemService(BatteryManager.class);
+ when(mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY))
+ .thenReturn(power_level);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/LowStorageSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/LowStorageSliceTest.java
index 99f2723..c8ec57a 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/LowStorageSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/LowStorageSliceTest.java
@@ -28,8 +28,8 @@
import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
+import com.android.settings.testutils.shadow.ShadowPrivateStorageInfo;
import com.android.settingslib.deviceinfo.PrivateStorageInfo;
-import com.android.settingslib.deviceinfo.StorageVolumeProvider;
import org.junit.After;
import org.junit.Before;
@@ -38,9 +38,6 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.Resetter;
@RunWith(RobolectricTestRunner.class)
public class LowStorageSliceTest {
@@ -104,26 +101,4 @@
assertThat(slice.hasHint(HINT_ERROR)).isTrue();
}
-
- @Implements(PrivateStorageInfo.class)
- public static class ShadowPrivateStorageInfo {
-
- private static PrivateStorageInfo sPrivateStorageInfo = null;
-
- @Resetter
- public static void reset() {
- sPrivateStorageInfo = null;
- }
-
- @Implementation
- public static PrivateStorageInfo getPrivateStorageInfo(
- StorageVolumeProvider storageVolumeProvider) {
- return sPrivateStorageInfo;
- }
-
- private static void setPrivateStorageInfo(
- PrivateStorageInfo privateStorageInfo) {
- sPrivateStorageInfo = privateStorageInfo;
- }
- }
}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java
index c4f1948..bddc91f 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileNetworkActivityTest.java
@@ -42,6 +42,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -121,6 +122,7 @@
}
@Test
+ @Ignore
public void updateBottomNavigationViewV2_oneSubscription_shouldNotCrash() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1);
@@ -130,6 +132,7 @@
}
@Test
+ @Ignore
public void updateBottomNavigationViewV2_twoSubscription_shouldNotCrash() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
@@ -139,6 +142,7 @@
}
@Test
+ @Ignore
public void switchFragment_switchBetweenTwoSubscriptions() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
@@ -154,6 +158,7 @@
}
@Test
+ @Ignore
public void switchFragment_subscriptionsUpdate_notifyByIntent() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
@@ -181,6 +186,7 @@
}
@Test
+ @Ignore
public void onSaveInstanceState_saveCurrentSubId() {
mSubscriptionManager.setActiveSubscriptionInfos(mSubscriptionInfo1, mSubscriptionInfo2);
diff --git a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
index 5e5e573..5221630 100644
--- a/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
+++ b/tests/robotests/src/com/android/settings/search/BaseSearchIndexProviderTest.java
@@ -28,6 +28,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.SearchIndexableRaw;
import org.junit.Before;
import org.junit.Test;
@@ -77,6 +78,12 @@
public String getPreferenceKey() {
return TEST_PREF_KEY;
}
+
+ @Override
+ public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
+ final SearchIndexableRaw raw = new SearchIndexableRaw(this.mContext);
+ rawData.add(raw);
+ }
}
@Test
@@ -190,4 +197,18 @@
assertThat(nonIndexableKeys).contains("pref_key_5");
}
+
+ @Test
+ public void getDynamicRawDataToIndex_noPreferenceController_shouldReturnEmptyList() {
+ assertThat(mIndexProvider.getDynamicRawDataToIndex(mContext, true)).isEmpty();
+ }
+
+ @Test
+ public void getDynamicRawDataToIndex_hasDynamicRaw_shouldNotEmpty() {
+ List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new AvailablePreferenceController(mContext));
+ doReturn(controllers).when(mIndexProvider).createPreferenceControllers(mContext);
+
+ assertThat(mIndexProvider.getDynamicRawDataToIndex(mContext, true)).isNotEmpty();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java b/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java
index e2b896a..c646a93 100644
--- a/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java
+++ b/tests/robotests/src/com/android/settings/search/actionbar/SearchMenuControllerTest.java
@@ -16,6 +16,8 @@
package com.android.settings.search.actionbar;
+import static com.android.settings.search.actionbar.SearchMenuController.MENU_SEARCH;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -69,7 +71,7 @@
Global.putInt(mActivity.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
when(mHost.getActivity()).thenReturn(mActivity);
- when(mMenu.add(Menu.NONE, Menu.NONE, 0 /* order */, R.string.search_menu))
+ when(mMenu.add(Menu.NONE, MENU_SEARCH, 0 /* order */, R.string.search_menu))
.thenReturn(mock(MenuItem.class));
}
@@ -78,7 +80,7 @@
SearchMenuController.init(mHost);
mHost.getSettingsLifecycle().onCreateOptionsMenu(mMenu, null /* inflater */);
- verify(mMenu).add(Menu.NONE, Menu.NONE, 0 /* order */, R.string.search_menu);
+ verify(mMenu).add(Menu.NONE, MENU_SEARCH, 0 /* order */, R.string.search_menu);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/security/screenlock/LockScreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/security/screenlock/LockScreenPreferenceControllerTest.java
index 45d0295..7ca9050 100644
--- a/tests/robotests/src/com/android/settings/security/screenlock/LockScreenPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/security/screenlock/LockScreenPreferenceControllerTest.java
@@ -22,7 +22,6 @@
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
-import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
import static com.google.common.truth.Truth.assertThat;
@@ -87,11 +86,11 @@
}
@Test
- public void getAvailabilityStatus_notSecure_lockscreenDisabled_DISABLED() {
+ public void getAvailabilityStatus_notSecure_lockscreenDisabled_AVAILABLE() {
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(false);
when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true);
- assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
@Test
@@ -112,12 +111,12 @@
}
@Test
- public void getAvailabilityStatus_secure_noLockScreen_DISABLED() {
+ public void getAvailabilityStatus_secure_noLockScreen_AVAILABLE() {
when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
when(mLockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
.thenReturn(PASSWORD_QUALITY_UNSPECIFIED);
- assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/slices/FakePreferenceController.java b/tests/robotests/src/com/android/settings/slices/FakePreferenceController.java
index 3e4da20..fc4ebc8 100644
--- a/tests/robotests/src/com/android/settings/slices/FakePreferenceController.java
+++ b/tests/robotests/src/com/android/settings/slices/FakePreferenceController.java
@@ -42,6 +42,11 @@
}
@Override
+ public boolean isPublicSlice() {
+ return true;
+ }
+
+ @Override
public boolean useDynamicSliceSummary() {
return true;
}
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
index 3ce964c..0c654a4 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataConverterTest.java
@@ -128,6 +128,7 @@
assertThat(fakeSlice.getSliceType()).isEqualTo(SliceData.SliceType.SLIDER);
assertThat(fakeSlice.getUnavailableSliceSubtitle()).isEqualTo(
"subtitleOfUnavailableSlice"); // from XML
+ assertThat(fakeSlice.isPublicSlice()).isTrue();
}
private void assertFakeA11ySlice(SliceData fakeSlice) {
diff --git a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
index 6074fe3..fa3f8b9 100644
--- a/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SliceDataTest.java
@@ -52,7 +52,8 @@
.setUri(URI)
.setPreferenceControllerClassName(PREF_CONTROLLER)
.setSliceType(SLICE_TYPE)
- .setUnavailableSliceSubtitle(UNAVAILABLE_SLICE_SUBTITLE);
+ .setUnavailableSliceSubtitle(UNAVAILABLE_SLICE_SUBTITLE)
+ .setIsPublicSlice(true);
SliceData data = builder.build();
@@ -67,6 +68,7 @@
assertThat(data.getPreferenceController()).isEqualTo(PREF_CONTROLLER);
assertThat(data.getSliceType()).isEqualTo(SLICE_TYPE);
assertThat(data.getUnavailableSliceSubtitle()).isEqualTo(UNAVAILABLE_SLICE_SUBTITLE);
+ assertThat(data.isPublicSlice()).isEqualTo(true);
}
@Test(expected = SliceData.InvalidSliceDataException.class)
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
index fad9c25..65214fc 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicesDatabaseHelperTest.java
@@ -75,6 +75,7 @@
IndexColumns.CONTROLLER,
IndexColumns.SLICE_TYPE,
IndexColumns.UNAVAILABLE_SLICE_SUBTITLE,
+ IndexColumns.PUBLIC_SLICE
};
assertThat(columnNames).isEqualTo(expectedNames);
diff --git a/tests/robotests/src/com/android/settings/slices/SlicesIndexerTest.java b/tests/robotests/src/com/android/settings/slices/SlicesIndexerTest.java
index 1501e45..3cb68c0 100644
--- a/tests/robotests/src/com/android/settings/slices/SlicesIndexerTest.java
+++ b/tests/robotests/src/com/android/settings/slices/SlicesIndexerTest.java
@@ -105,8 +105,8 @@
}
@Test
- public void testInsertSliceData_mockDataInserted() {
- final List<SliceData> sliceData = getDummyIndexableData();
+ public void testInsertSliceData_nonPublicSlice_mockDataInserted() {
+ final List<SliceData> sliceData = getDummyIndexableData(false);
doReturn(sliceData).when(mManager).getSliceData();
mManager.run();
@@ -140,6 +140,53 @@
assertThat(cursor.getString(
cursor.getColumnIndex(IndexColumns.UNAVAILABLE_SLICE_SUBTITLE)))
.isEqualTo(UNAVAILABLE_SLICE_SUBTITLE);
+ assertThat(cursor.getInt(
+ cursor.getColumnIndex(IndexColumns.PUBLIC_SLICE))).isEqualTo(0);
+ cursor.moveToNext();
+ }
+ } finally {
+ db.close();
+ }
+ }
+
+ @Test
+ public void insertSliceData_publicSlice_mockDataInserted() {
+ final List<SliceData> sliceData = getDummyIndexableData(true);
+ doReturn(sliceData).when(mManager).getSliceData();
+
+ mManager.run();
+
+ final SQLiteDatabase db = SlicesDatabaseHelper.getInstance(mContext).getWritableDatabase();
+ try (Cursor cursor = db.rawQuery("SELECT * FROM slices_index", null)) {
+ assertThat(cursor.getCount()).isEqualTo(sliceData.size());
+
+ cursor.moveToFirst();
+ for (int i = 0; i < sliceData.size(); i++) {
+ assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.KEY)))
+ .isEqualTo(KEYS[i]);
+ assertThat(cursor.getString(cursor.getColumnIndex(IndexColumns.TITLE)))
+ .isEqualTo(TITLES[i]);
+ assertThat(
+ cursor.getString(cursor.getColumnIndex(IndexColumns.FRAGMENT)))
+ .isEqualTo(FRAGMENT_NAME);
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(IndexColumns.SCREENTITLE))).isEqualTo(SCREEN_TITLE);
+ assertThat(
+ cursor.getString(cursor.getColumnIndex(IndexColumns.KEYWORDS)))
+ .isEqualTo(KEYWORDS);
+ assertThat(
+ cursor.getInt(cursor.getColumnIndex(IndexColumns.ICON_RESOURCE)))
+ .isEqualTo(ICON);
+ assertThat(
+ cursor.getString(cursor.getColumnIndex(IndexColumns.CONTROLLER)))
+ .isEqualTo(PREF_CONTROLLER);
+ assertThat(cursor.getInt(cursor.getColumnIndex(IndexColumns.SLICE_TYPE)))
+ .isEqualTo(SLICE_TYPE);
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(IndexColumns.UNAVAILABLE_SLICE_SUBTITLE)))
+ .isEqualTo(UNAVAILABLE_SLICE_SUBTITLE);
+ assertThat(cursor.getInt(
+ cursor.getColumnIndex(IndexColumns.PUBLIC_SLICE))).isEqualTo(1);
cursor.moveToNext();
}
} finally {
@@ -162,7 +209,7 @@
db.close();
}
- private List<SliceData> getDummyIndexableData() {
+ private List<SliceData> getDummyIndexableData(boolean isPublicSlice) {
final List<SliceData> sliceData = new ArrayList<>();
final SliceData.Builder builder = new SliceData.Builder()
.setSummary(SUMMARY)
@@ -175,6 +222,10 @@
.setSliceType(SLICE_TYPE)
.setUnavailableSliceSubtitle(UNAVAILABLE_SLICE_SUBTITLE);
+ if (isPublicSlice) {
+ builder.setIsPublicSlice(true);
+ }
+
for (int i = 0; i < KEYS.length; i++) {
builder.setKey(KEYS[i]).setTitle(TITLES[i]);
sliceData.add(builder.build());
diff --git a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
index 5b7c863..9c91602 100644
--- a/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/AudioOutputSwitchPreferenceControllerTest.java
@@ -16,7 +16,6 @@
package com.android.settings.sound;
-import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO;
import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
import static android.media.AudioSystem.STREAM_MUSIC;
@@ -45,7 +44,6 @@
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
-import com.android.settings.R;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.testutils.shadow.ShadowAudioManager;
@@ -66,7 +64,6 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowBluetoothDevice;
@@ -246,7 +243,8 @@
public void isStreamFromOutputDevice_outputDeviceIsBtScoHeadset_shouldReturnTrue() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_SCO_HEADSET);
- assertThat(mController.isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_ALL_SCO)).isTrue();
+ assertThat(mController.isStreamFromOutputDevice(
+ STREAM_MUSIC, DEVICE_OUT_BLUETOOTH_SCO_HEADSET)).isTrue();
}
/**
@@ -407,4 +405,4 @@
return TEST_KEY;
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
index d251480..f2a932e 100644
--- a/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/system/FactoryResetPreferenceControllerTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.provider.Settings;
import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -81,7 +83,7 @@
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 1);
// Indicate the user is a demo user.
- mShadowUserManager.setIsDemoUser(true);
+ mShadowUserManager.addUser(UserHandle.myUserId(), "test", UserInfo.FLAG_DEMO);
assertThat(mController.isAvailable()).isTrue();
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPrivateStorageInfo.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPrivateStorageInfo.java
new file mode 100644
index 0000000..1baf3cb
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowPrivateStorageInfo.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils.shadow;
+
+import com.android.settingslib.deviceinfo.PrivateStorageInfo;
+import com.android.settingslib.deviceinfo.StorageVolumeProvider;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+@Implements(PrivateStorageInfo.class)
+public class ShadowPrivateStorageInfo {
+
+ private static PrivateStorageInfo sPrivateStorageInfo = null;
+
+ @Resetter
+ public static void reset() {
+ sPrivateStorageInfo = null;
+ }
+
+ @Implementation
+ protected static PrivateStorageInfo getPrivateStorageInfo(
+ StorageVolumeProvider storageVolumeProvider) {
+ return sPrivateStorageInfo;
+ }
+
+ public static void setPrivateStorageInfo(
+ PrivateStorageInfo privateStorageInfo) {
+ sPrivateStorageInfo = privateStorageInfo;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java
index 61ce13d..4f0767c 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowThreadUtils.java
@@ -16,16 +16,12 @@
package com.android.settings.testutils.shadow;
-import android.util.Log;
-
import com.android.settingslib.utils.ThreadUtils;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
-import java.util.concurrent.Callable;
-
@Implements(ThreadUtils.class)
public class ShadowThreadUtils {
@@ -43,15 +39,6 @@
}
@Implementation
- protected static void postOnBackgroundThread(Callable callable) {
- try {
- callable.call();
- } catch (Exception e) {
- Log.e(TAG, e.getMessage(), e);
- }
- }
-
- @Implementation
protected static void postOnMainThread(Runnable runnable) {
runnable.run();
}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTileUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTileUtils.java
index e46d72f..e1b815e 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTileUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowTileUtils.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.IContentProvider;
import android.net.Uri;
+import android.os.Bundle;
import android.util.Pair;
import com.android.settings.R;
@@ -35,6 +36,9 @@
public static final String MOCK_SUMMARY = "summary";
+ private static boolean sChecked;
+ private static Bundle sResult;
+
@Implementation
protected static String getTextFromUri(Context context, Uri uri,
Map<String, IContentProvider> providerMap, String key) {
@@ -46,4 +50,29 @@
Uri uri, Map<String, IContentProvider> providerMap) {
return Pair.create(RuntimeEnvironment.application.getPackageName(), R.drawable.ic_settings_accent);
}
+
+ @Implementation
+ public static boolean getBooleanFromUri(Context context, Uri uri,
+ Map<String, IContentProvider> providerMap, String key) {
+ return sChecked;
+ }
+
+ @Implementation
+ public static Bundle putBooleanToUri(Context context, Uri uri,
+ Map<String, IContentProvider> providerMap, String key, boolean value) {
+ sChecked = value;
+ return sResult;
+ }
+
+ public static boolean getProviderChecked() {
+ return sChecked;
+ }
+
+ public static void setProviderChecked(boolean value) {
+ sChecked = value;
+ }
+
+ public static void setResultBundle(Bundle result) {
+ sResult = result;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java b/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java
deleted file mode 100644
index a2e9de7..0000000
--- a/tests/robotests/src/com/android/settings/widget/UpdatableListPreferenceDialogFragmentTest.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.widget;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.widget.ArrayAdapter;
-
-import androidx.preference.ListPreference;
-
-import com.android.internal.logging.nano.MetricsProto;
-import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
-import java.util.ArrayList;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowBluetoothUtils.class)
-public class UpdatableListPreferenceDialogFragmentTest {
-
- private static final String KEY = "Test_Key";
- @Mock
- private UpdatableListPreferenceDialogFragment mUpdatableListPrefDlgFragment;
- private Context mContext;
- private ArrayAdapter mAdapter;
- private ArrayList<CharSequence> mEntries;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
-
- mUpdatableListPrefDlgFragment = spy(UpdatableListPreferenceDialogFragment
- .newInstance(KEY, MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES));
- mEntries = spy(new ArrayList<>());
- mUpdatableListPrefDlgFragment.setEntries(mEntries);
- mUpdatableListPrefDlgFragment.
- setMetricsCategory(mUpdatableListPrefDlgFragment.getArguments());
- initAdapter();
- }
-
- private void initAdapter() {
- mAdapter = spy(new ArrayAdapter<>(
- mContext,
- com.android.internal.R.layout.select_dialog_singlechoice,
- mEntries));
- mUpdatableListPrefDlgFragment.setAdapter(mAdapter);
- }
-
- @Test
- public void getMetricsCategory() {
- assertThat(mUpdatableListPrefDlgFragment.getMetricsCategory())
- .isEqualTo(MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES);
- }
-
- @Test
- public void onListPreferenceUpdated_verifyAdapterCanBeUpdate() {
- assertThat(mUpdatableListPrefDlgFragment.getAdapter().getCount()).isEqualTo(0);
-
- ListPreference listPreference = new ListPreference(mContext);
- final CharSequence[] charSequences = {"Test_DEVICE_1", "Test_DEVICE_2"};
- listPreference.setEntries(charSequences);
- mUpdatableListPrefDlgFragment.onListPreferenceUpdated(listPreference);
-
- assertThat(mUpdatableListPrefDlgFragment.getAdapter().getCount()).isEqualTo(2);
- }
-
- @Test
- public void onDialogClosed_emptyPreference() {
- mUpdatableListPrefDlgFragment.onDialogClosed(false);
-
- verify(mUpdatableListPrefDlgFragment, never()).getListPreference();
- }
-}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
index ab4bea8..82d76b3 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigControllerTest.java
@@ -31,6 +31,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiEnterpriseConfig;
import android.net.wifi.WifiEnterpriseConfig.Eap;
+import android.net.wifi.WifiEnterpriseConfig.Phase2;
import android.net.wifi.WifiManager;
import android.os.ServiceSpecificException;
import android.security.KeyStore;
@@ -542,10 +543,26 @@
mController = new TestWifiConfigController(mConfigUiBase, mView, mAccessPoint,
WifiConfigUiBase.MODE_MODIFY);
final Spinner eapMethodSpinner = mView.findViewById(R.id.method);
+ final Spinner phase2Spinner = mView.findViewById(R.id.phase2);
+ WifiConfiguration wifiConfiguration;
- eapMethodSpinner.setSelection(Eap.TLS);
+ // Test EAP method PEAP
+ eapMethodSpinner.setSelection(Eap.PEAP);
+ phase2Spinner.setSelection(WifiConfigController.WIFI_PEAP_PHASE2_MSCHAPV2);
+ wifiConfiguration = mController.getConfig();
- assertThat(eapMethodSpinner.getSelectedItemPosition()).isEqualTo(Eap.TLS);
+ assertThat(wifiConfiguration.enterpriseConfig.getEapMethod()).isEqualTo(Eap.PEAP);
+ assertThat(wifiConfiguration.enterpriseConfig.getPhase2Method()).isEqualTo(
+ Phase2.MSCHAPV2);
+
+ // Test EAP method TTLS
+ eapMethodSpinner.setSelection(Eap.TTLS);
+ phase2Spinner.setSelection(WifiConfigController.WIFI_TTLS_PHASE2_MSCHAPV2);
+ wifiConfiguration = mController.getConfig();
+
+ assertThat(wifiConfiguration.enterpriseConfig.getEapMethod()).isEqualTo(Eap.TTLS);
+ assertThat(wifiConfiguration.enterpriseConfig.getPhase2Method()).isEqualTo(
+ Phase2.MSCHAPV2);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
new file mode 100644
index 0000000..8d15224
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2Test.java
@@ -0,0 +1,1905 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.inOrder;
+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.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkRequest;
+import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
+import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.widget.ActionButtonsPreference;
+import com.android.settingslib.widget.LayoutPreference;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Matchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowToast;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowDevicePolicyManager.class, ShadowEntityHeaderController.class})
+public class WifiDetailPreferenceController2Test {
+
+ private static final int LEVEL = 1;
+ private static final int RSSI = -55;
+ private static final int TX_LINK_SPEED = 123;
+ private static final int RX_LINK_SPEED = 54;
+ private static final String SSID = "ssid";
+ private static final String MAC_ADDRESS = "01:23:45:67:89:ab";
+ private static final String RANDOMIZED_MAC_ADDRESS = "RANDOMIZED_MAC_ADDRESS";
+ private static final String FACTORY_MAC_ADDRESS = "FACTORY_MAC_ADDRESS";
+ private static final String SECURITY = "None";
+ private static final String FQDN = "fqdn";
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private PreferenceScreen mMockScreen;
+
+ @Mock
+ private AccessPoint mMockAccessPoint;
+ @Mock
+ private FragmentActivity mMockActivity;
+ @Mock
+ private ConnectivityManager mMockConnectivityManager;
+ @Mock
+ private Network mMockNetwork;
+ @Mock
+ private NetworkInfo mMockNetworkInfo;
+ @Mock
+ private WifiConfiguration mMockWifiConfig;
+ @Mock
+ private WifiInfo mMockWifiInfo;
+ @Mock
+ private WifiNetworkDetailsFragment2 mMockFragment;
+ @Mock
+ private WifiManager mMockWifiManager;
+ @Mock
+ private WifiTracker mMockWifiTracker;
+ @Mock
+ private MetricsFeatureProvider mMockMetricsFeatureProvider;
+ @Mock
+ private WifiDetailPreferenceController2.IconInjector mMockIconInjector;
+ @Mock
+ private MacAddress mMockMacAddress;
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private EntityHeaderController mMockHeaderController;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private LayoutPreference mMockHeaderLayoutPreference;
+ @Mock
+ private ImageView mMockHeaderIcon;
+
+ @Mock
+ private ActionButtonsPreference mMockButtonsPref;
+ @Mock
+ private Preference mMockSignalStrengthPref;
+ @Mock
+ private Preference mMockTxLinkSpeedPref;
+ @Mock
+ private Preference mMockRxLinkSpeedPref;
+ @Mock
+ private Preference mMockFrequencyPref;
+ @Mock
+ private Preference mMockSecurityPref;
+ @Mock
+ private Preference mMockSsidPref;
+ @Mock
+ private Preference mMockMacAddressPref;
+ @Mock
+ private Preference mMockIpAddressPref;
+ @Mock
+ private Preference mMockGatewayPref;
+ @Mock
+ private Preference mMockSubnetPref;
+ @Mock
+ private Preference mMockDnsPref;
+ @Mock
+ private PreferenceCategory mMockIpv6Category;
+ @Mock
+ private Preference mMockIpv6AddressesPref;
+ @Mock
+ private PackageManager mMockPackageManager;
+
+ @Captor
+ private ArgumentCaptor<NetworkCallback> mCallbackCaptor;
+ @Captor
+ private ArgumentCaptor<View.OnClickListener> mForgetClickListener;
+
+ private Context mContext;
+ private Lifecycle mLifecycle;
+ private LifecycleOwner mLifecycleOwner;
+ private LinkProperties mLinkProperties;
+ private WifiDetailPreferenceController2 mController;
+
+ // This class exists so that these values can be made static final. They can't be static final
+ // members of the test class, because any attempt to call IpPrefix or RouteInfo constructors
+ // during static initialization of the test class results in NoSuchMethorError being thrown
+ // when the test is run.
+ private static class Constants {
+ static final int IPV4_PREFIXLEN = 25;
+ static final LinkAddress IPV4_ADDR;
+ static final Inet4Address IPV4_GATEWAY;
+ static final RouteInfo IPV4_DEFAULT;
+ static final RouteInfo IPV4_SUBNET;
+ static final LinkAddress IPV6_LINKLOCAL;
+ static final LinkAddress IPV6_GLOBAL1;
+ static final LinkAddress IPV6_GLOBAL2;
+ static final InetAddress IPV4_DNS1;
+ static final InetAddress IPV4_DNS2;
+ static final InetAddress IPV6_DNS;
+
+ private static LinkAddress ipv6LinkAddress(String addr) throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), 64);
+ }
+
+ private static LinkAddress ipv4LinkAddress(String addr, int prefixlen)
+ throws UnknownHostException {
+ return new LinkAddress(InetAddress.getByName(addr), prefixlen);
+ }
+
+ static {
+ try {
+ // We create our test constants in these roundabout ways because the robolectric
+ // shadows don't contain NetworkUtils.parseNumericAddress and other utility methods,
+ // so the easy ways to do things fail with NoSuchMethodError.
+ IPV4_ADDR = ipv4LinkAddress("192.0.2.2", IPV4_PREFIXLEN);
+ IPV4_GATEWAY = (Inet4Address) InetAddress.getByName("192.0.2.127");
+
+ final Inet4Address any4 = (Inet4Address) InetAddress.getByName("0.0.0.0");
+ IpPrefix subnet = new IpPrefix(IPV4_ADDR.getAddress(), IPV4_PREFIXLEN);
+ IPV4_SUBNET = new RouteInfo(subnet, any4);
+ IPV4_DEFAULT = new RouteInfo(new IpPrefix(any4, 0), IPV4_GATEWAY);
+
+ IPV6_LINKLOCAL = ipv6LinkAddress("fe80::211:25ff:fef8:7cb2%1");
+ IPV6_GLOBAL1 = ipv6LinkAddress("2001:db8:1::211:25ff:fef8:7cb2");
+ IPV6_GLOBAL2 = ipv6LinkAddress("2001:db8:1::3dfe:8902:f98f:739d");
+
+ IPV4_DNS1 = InetAddress.getByName("8.8.8.8");
+ IPV4_DNS2 = InetAddress.getByName("8.8.4.4");
+ IPV6_DNS = InetAddress.getByName("2001:4860:4860::64");
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Invalid hardcoded IP addresss: " + e);
+ }
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ mLifecycleOwner = () -> mLifecycle;
+ mLifecycle = new Lifecycle(mLifecycleOwner);
+
+ when(mContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockAccessPoint.getConfig()).thenReturn(mMockWifiConfig);
+ when(mMockAccessPoint.getLevel()).thenReturn(LEVEL);
+ when(mMockAccessPoint.getSecurityString(false)).thenReturn(SECURITY);
+ when(mMockAccessPoint.getSsidStr()).thenReturn(SSID);
+ when(mMockConnectivityManager.getNetworkInfo(any(Network.class)))
+ .thenReturn(mMockNetworkInfo);
+ doNothing().when(mMockConnectivityManager).registerNetworkCallback(
+ nullable(NetworkRequest.class), mCallbackCaptor.capture(), nullable(Handler.class));
+ mMockButtonsPref = createMock();
+ when(mMockButtonsPref.setButton1OnClickListener(mForgetClickListener.capture()))
+ .thenReturn(mMockButtonsPref);
+
+ when(mMockWifiInfo.getTxLinkSpeedMbps()).thenReturn(TX_LINK_SPEED);
+ when(mMockWifiInfo.getRxLinkSpeedMbps()).thenReturn(RX_LINK_SPEED);
+ when(mMockWifiInfo.getRssi()).thenReturn(RSSI);
+ when(mMockWifiInfo.getMacAddress()).thenReturn(MAC_ADDRESS);
+ when(mMockWifiManager.getConnectionInfo()).thenReturn(mMockWifiInfo);
+
+ when(mMockWifiManager.getCurrentNetwork()).thenReturn(mMockNetwork);
+ mLinkProperties = new LinkProperties();
+ when(mMockConnectivityManager.getLinkProperties(mMockNetwork)).thenReturn(mLinkProperties);
+
+ when(mMockFragment.getActivity()).thenReturn(mMockActivity);
+
+ ShadowEntityHeaderController.setUseMock(mMockHeaderController);
+ // builder pattern
+ when(mMockHeaderController.setRecyclerView(mMockFragment.getListView(), mLifecycle))
+ .thenReturn(mMockHeaderController);
+ when(mMockHeaderController.setSummary(anyString())).thenReturn(mMockHeaderController);
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(new ColorDrawable());
+
+ setupMockedPreferenceScreen();
+ }
+
+ private void setUpForConnectedNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ when(mMockAccessPoint.isReachable()).thenReturn(true);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private void setUpForDisconnectedNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ when(mMockAccessPoint.isReachable()).thenReturn(true);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private void setUpForNotInRangeNetwork() {
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(false);
+ when(mMockAccessPoint.isReachable()).thenReturn(false);
+
+ mController = newWifiDetailPreferenceController2();
+ }
+
+ private WifiDetailPreferenceController2 newWifiDetailPreferenceController2() {
+ return new WifiDetailPreferenceController2(
+ mMockAccessPoint,
+ mMockConnectivityManager,
+ mContext,
+ mMockFragment,
+ null, // Handler
+ mLifecycle,
+ mMockWifiManager,
+ mMockMetricsFeatureProvider,
+ mMockIconInjector);
+ }
+
+ private void setupMockedPreferenceScreen() {
+ when(mMockScreen.getPreferenceManager().getContext()).thenReturn(mContext);
+
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_HEADER))
+ .thenReturn(mMockHeaderLayoutPreference);
+ when(mMockHeaderLayoutPreference.findViewById(R.id.entity_header_icon))
+ .thenReturn(mMockHeaderIcon);
+
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_BUTTONS_PREF))
+ .thenReturn(mMockButtonsPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SIGNAL_STRENGTH_PREF))
+ .thenReturn(mMockSignalStrengthPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_TX_LINK_SPEED))
+ .thenReturn(mMockTxLinkSpeedPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_RX_LINK_SPEED))
+ .thenReturn(mMockRxLinkSpeedPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_FREQUENCY_PREF))
+ .thenReturn(mMockFrequencyPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SECURITY_PREF))
+ .thenReturn(mMockSecurityPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SSID_PREF))
+ .thenReturn(mMockSsidPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_MAC_ADDRESS_PREF))
+ .thenReturn(mMockMacAddressPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IP_ADDRESS_PREF))
+ .thenReturn(mMockIpAddressPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_GATEWAY_PREF))
+ .thenReturn(mMockGatewayPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_SUBNET_MASK_PREF))
+ .thenReturn(mMockSubnetPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_DNS_PREF))
+ .thenReturn(mMockDnsPref);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_CATEGORY))
+ .thenReturn(mMockIpv6Category);
+ when(mMockScreen.findPreference(WifiDetailPreferenceController2.KEY_IPV6_ADDRESSES_PREF))
+ .thenReturn(mMockIpv6AddressesPref);
+ }
+
+ private void displayAndResume() {
+ mController.displayPreference(mMockScreen);
+ mController.onResume();
+ }
+
+ @Test
+ public void isAvailable_shouldAlwaysReturnTrue() {
+ setUpForConnectedNetwork();
+ mController.displayPreference(mMockScreen);
+
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void securityPreference_stringShouldBeSet() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockSecurityPref).setSummary(SECURITY);
+ }
+
+ @Test
+ public void latestWifiInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+ }
+
+ @Test
+ public void latestWifiInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, never()).getConnectionInfo();
+ }
+
+ @Test
+ public void latestWifiInfo_shouldNotBeFetchedInDisplayPreferenceForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockWifiManager, never()).getConnectionInfo();
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldBeFetchedInDisplayPreferenceForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldNotBeFetchedInDisplayPreferenceForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, never()).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void latestNetworkInfo_shouldNotBeFetchedInDisplayPreferenceForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, never()).getNetworkInfo(any(Network.class));
+ }
+
+ @Test
+ public void networkCallback_shouldBeRegisteredOnResume() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).registerNetworkCallback(
+ nullable(NetworkRequest.class), mCallbackCaptor.capture(), nullable(Handler.class));
+ }
+
+ @Test
+ public void networkCallback_shouldBeUnregisteredOnPause() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+ mController.onPause();
+
+ verify(mMockConnectivityManager, times(1))
+ .unregisterNetworkCallback(mCallbackCaptor.getValue());
+ }
+
+ @Test
+ public void entityHeader_shouldHaveIconSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setIcon(expectedIcon);
+ }
+
+ @Test
+ public void entityHeader_shouldHaveIconSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ Drawable expectedIcon = mMockIconInjector.getIcon(LEVEL);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setIcon(expectedIcon);
+ }
+
+ @Test
+ public void entityHeader_shouldNotHaveIconSetForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockHeaderController, never()).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void entityHeader_shouldHaveLabelSetToTitle() {
+ setUpForConnectedNetwork();
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setLabel(label);
+ }
+
+ @Test
+ public void entityHeader_shouldHaveSummarySet() {
+ setUpForConnectedNetwork();
+ String summary = "summary";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ displayAndResume();
+
+ verify(mMockHeaderController).setSummary(summary);
+ }
+
+ @Test
+ public void entityHeader_shouldConvertSavedAsDisconnected() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockAccessPoint, times(1)).getSettingsSummary(true /*convertSavedAsDisconnected*/);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveIconSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveIconSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldNotHaveIconSetForOutOfRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref, never()).setIcon(any(Drawable.class));
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedStrength =
+ mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldHaveDetailTextSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ String expectedStrength =
+ mContext.getResources().getStringArray(R.array.wifi_signal)[LEVEL];
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref).setSummary(expectedStrength);
+ }
+
+ @Test
+ public void signalStrengthPref_shouldNotHaveDetailTextSetForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockSignalStrengthPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void linkSpeedPref_shouldNotShowIfNotSet() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.getTxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN);
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ }
+
+ @Test
+ public void linkSpeedPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedLinkSpeed = mContext.getString(R.string.tx_link_speed, TX_LINK_SPEED);
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(true);
+ verify(mMockTxLinkSpeedPref).setSummary(expectedLinkSpeed);
+ }
+
+ @Test
+ public void linkSpeedPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ verify(mMockTxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void linkSpeedPref_shouldInvisibleForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockTxLinkSpeedPref).setVisible(false);
+ verify(mMockTxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldNotShowIfNotSet() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.getRxLinkSpeedMbps()).thenReturn(WifiInfo.LINK_SPEED_UNKNOWN);
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ String expectedLinkSpeed = mContext.getString(R.string.rx_link_speed, RX_LINK_SPEED);
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(true);
+ verify(mMockRxLinkSpeedPref).setSummary(expectedLinkSpeed);
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ verify(mMockRxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void rxLinkSpeedPref_shouldInvisibleForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ verify(mMockRxLinkSpeedPref).setVisible(false);
+ verify(mMockRxLinkSpeedPref, never()).setSummary(any(String.class));
+ }
+
+ @Test
+ public void ssidPref_shouldHaveDetailTextSetForPasspointR1() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(false);
+
+ displayAndResume();
+
+ verify(mMockSsidPref, times(1)).setSummary(SSID);
+ verify(mMockSsidPref, times(1)).setVisible(true);
+ }
+
+ @Test
+ public void ssidPref_shouldHaveDetailTextSetForPasspointR2() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(false);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(true);
+
+ displayAndResume();
+
+ verify(mMockSsidPref, times(1)).setSummary(SSID);
+ verify(mMockSsidPref, times(1)).setVisible(true);
+ }
+
+ @Test
+ public void ssidPref_shouldNotShowIfNotPasspoint() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(false);
+ when(mMockAccessPoint.isOsuProvider()).thenReturn(false);
+
+ displayAndResume();
+
+ verify(mMockSsidPref).setVisible(false);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(MAC_ADDRESS);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleAsRandomizedForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockMacAddress.toString()).thenReturn(RANDOMIZED_MAC_ADDRESS);
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(RANDOMIZED_MAC_ADDRESS);
+ }
+
+ @Test
+ public void macAddressPref_shouldVisibleAsFactoryForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ when(mMockWifiManager.getFactoryMacAddresses())
+ .thenReturn(new String[]{FACTORY_MAC_ADDRESS});
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setVisible(true);
+ verify(mMockMacAddressPref).setSummary(FACTORY_MAC_ADDRESS);
+ }
+
+ @Test
+ public void ipAddressPref_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
+
+ displayAndResume();
+
+ verify(mMockIpAddressPref).setSummary(Constants.IPV4_ADDR.getAddress().getHostAddress());
+ verify(mMockIpAddressPref).setVisible(true);
+ }
+
+ @Test
+ public void ipAddressPref_shouldInvisibleForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockIpAddressPref).setVisible(false);
+ }
+
+ @Test
+ public void gatewayAndSubnet_shouldHaveDetailTextSetForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV4_ADDR);
+ mLinkProperties.addRoute(Constants.IPV4_DEFAULT);
+ mLinkProperties.addRoute(Constants.IPV4_SUBNET);
+
+ displayAndResume();
+
+ verify(mMockSubnetPref).setSummary("255.255.255.128");
+ verify(mMockGatewayPref).setSummary("192.0.2.127");
+ verify(mMockSubnetPref).setVisible(true);
+ }
+
+ @Test
+ public void gatewayAndSubnet_shouldInvisibleSetForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockSubnetPref).setVisible(false);
+ }
+
+ @Test
+ public void dnsServersPref_shouldHaveDetailTextSetForConnectedNetwork()
+ throws UnknownHostException {
+ setUpForConnectedNetwork();
+ mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[] {8, 8, 4, 4}));
+ mLinkProperties.addDnsServer(InetAddress.getByAddress(new byte[] {8, 8, 8, 8}));
+ mLinkProperties.addDnsServer(Constants.IPV6_DNS);
+
+ displayAndResume();
+
+ verify(mMockDnsPref).setSummary(
+ "8.8.4.4\n" + "8.8.8.8\n" + Constants.IPV6_DNS.getHostAddress());
+ verify(mMockDnsPref).setVisible(true);
+ }
+
+ @Test
+ public void dnsServersPref_shouldInvisibleSetForDisconnectedNetwork()
+ throws UnknownHostException {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockDnsPref).setVisible(false);
+ }
+
+ @Test
+ public void noCurrentNetwork_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ when(mMockWifiManager.getCurrentNetwork()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void noLinkProperties_allIpDetailsHidden() {
+ setUpForConnectedNetwork();
+ when(mMockConnectivityManager.getLinkProperties(mMockNetwork)).thenReturn(null);
+ reset(mMockIpv6Category, mMockIpAddressPref, mMockSubnetPref, mMockGatewayPref,
+ mMockDnsPref);
+
+ displayAndResume();
+
+ verify(mMockIpv6Category).setVisible(false);
+ verify(mMockIpAddressPref).setVisible(false);
+ verify(mMockSubnetPref).setVisible(false);
+ verify(mMockGatewayPref).setVisible(false);
+ verify(mMockDnsPref).setVisible(false);
+ verify(mMockIpv6Category, never()).setVisible(true);
+ verify(mMockIpAddressPref, never()).setVisible(true);
+ verify(mMockSubnetPref, never()).setVisible(true);
+ verify(mMockGatewayPref, never()).setVisible(true);
+ verify(mMockDnsPref, never()).setVisible(true);
+ }
+
+ @Test
+ public void disconnectedNetwork_allIpDetailsHidden() {
+ setUpForDisconnectedNetwork();
+ reset(mMockIpv6Category, mMockIpAddressPref, mMockSubnetPref, mMockGatewayPref,
+ mMockDnsPref);
+
+ displayAndResume();
+
+ verify(mMockIpv6Category).setVisible(false);
+ verify(mMockIpAddressPref).setVisible(false);
+ verify(mMockSubnetPref).setVisible(false);
+ verify(mMockGatewayPref).setVisible(false);
+ verify(mMockDnsPref).setVisible(false);
+ verify(mMockIpv6Category, never()).setVisible(true);
+ verify(mMockIpAddressPref, never()).setVisible(true);
+ verify(mMockSubnetPref, never()).setVisible(true);
+ verify(mMockGatewayPref, never()).setVisible(true);
+ verify(mMockDnsPref, never()).setVisible(true);
+ }
+
+ // Convenience method to convert a LinkAddress to a string without a prefix length.
+ private String asString(LinkAddress l) {
+ return l.getAddress().getHostAddress();
+ }
+
+ // Pretend that the NetworkCallback was triggered with a new copy of lp. We need to create a
+ // new copy because the code only updates if !mLinkProperties.equals(lp).
+ private void updateLinkProperties(LinkProperties lp) {
+ mCallbackCaptor.getValue().onLinkPropertiesChanged(mMockNetwork, new LinkProperties(lp));
+ }
+
+ private void updateNetworkCapabilities(NetworkCapabilities nc) {
+ mCallbackCaptor.getValue().onCapabilitiesChanged(mMockNetwork, new NetworkCapabilities(nc));
+ }
+
+ private NetworkCapabilities makeNetworkCapabilities() {
+ NetworkCapabilities nc = new NetworkCapabilities();
+ nc.clearAll();
+ nc.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ return nc;
+ }
+
+ private void verifyDisplayedIpv6Addresses(InOrder inOrder, LinkAddress... addresses) {
+ String text = Arrays.stream(addresses)
+ .map(address -> asString(address))
+ .collect(Collectors.joining("\n"));
+ inOrder.verify(mMockIpv6AddressesPref).setSummary(text);
+ }
+
+ @Test
+ public void onLinkPropertiesChanged_updatesFields() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ InOrder inOrder = inOrder(mMockIpAddressPref, mMockGatewayPref, mMockSubnetPref,
+ mMockDnsPref, mMockIpv6Category, mMockIpv6AddressesPref);
+
+ LinkProperties lp = new LinkProperties();
+
+ lp.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder, Constants.IPV6_LINKLOCAL);
+ inOrder.verify(mMockIpv6Category).setVisible(true);
+
+ lp.addRoute(Constants.IPV4_DEFAULT);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockGatewayPref).setSummary(Constants.IPV4_GATEWAY.getHostAddress());
+ inOrder.verify(mMockGatewayPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV4_ADDR);
+ lp.addRoute(Constants.IPV4_SUBNET);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockIpAddressPref).setSummary(asString(Constants.IPV4_ADDR));
+ inOrder.verify(mMockIpAddressPref).setVisible(true);
+ inOrder.verify(mMockSubnetPref).setSummary("255.255.255.128");
+ inOrder.verify(mMockSubnetPref).setVisible(true);
+
+ lp.addLinkAddress(Constants.IPV6_GLOBAL1);
+ lp.addLinkAddress(Constants.IPV6_GLOBAL2);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL1,
+ Constants.IPV6_GLOBAL2);
+
+ lp.removeLinkAddress(Constants.IPV6_GLOBAL1);
+ updateLinkProperties(lp);
+ verifyDisplayedIpv6Addresses(inOrder,
+ Constants.IPV6_LINKLOCAL,
+ Constants.IPV6_GLOBAL2);
+
+ lp.addDnsServer(Constants.IPV6_DNS);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockDnsPref).setSummary(Constants.IPV6_DNS.getHostAddress());
+ inOrder.verify(mMockDnsPref).setVisible(true);
+
+ lp.addDnsServer(Constants.IPV4_DNS1);
+ lp.addDnsServer(Constants.IPV4_DNS2);
+ updateLinkProperties(lp);
+ inOrder.verify(mMockDnsPref).setSummary(
+ Constants.IPV6_DNS.getHostAddress() + "\n"
+ + Constants.IPV4_DNS1.getHostAddress() + "\n"
+ + Constants.IPV4_DNS2.getHostAddress());
+ inOrder.verify(mMockDnsPref).setVisible(true);
+ }
+
+ @Test
+ public void onCapabilitiesChanged_callsRefreshIfNecessary() {
+ setUpForConnectedNetwork();
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ when(mMockConnectivityManager.getNetworkCapabilities(mMockNetwork))
+ .thenReturn(new NetworkCapabilities(nc));
+
+ String summary = "Connected, no Internet";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ InOrder inOrder = inOrder(mMockHeaderController);
+ displayAndResume();
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // Check that an irrelevant capability update does not update the access point summary, as
+ // doing so could cause unnecessary jank...
+ summary = "Connected";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController, never()).setSummary(any(CharSequence.class));
+
+ // ... but that if the network validates, then we do refresh.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ summary = "Connected, no Internet";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+
+ // Another irrelevant update won't cause the UI to refresh...
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController, never()).setSummary(any(CharSequence.class));
+
+ // ... but if the network is no longer validated, then we display "connected, no Internet".
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // UI will be refreshed when private DNS is broken.
+ summary = "Private DNS server cannot be accessed";
+ when(mMockAccessPoint.getSettingsSummary(true /* convertSavedAsDisconnected */))
+ .thenReturn(summary);
+ nc.setPrivateDnsBroken(true);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // UI will be refreshed when device connects to a partial connectivity network.
+ summary = "Limited connection";
+ when(mMockAccessPoint.getSettingsSummary(true /*convertSavedAsDisconnected*/))
+ .thenReturn(summary);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+
+ // Although UI will be refreshed when network become validated. The Settings should
+ // continue to display "Limited connection" if network still provides partial connectivity.
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockHeaderController).setSummary(summary);
+ }
+
+ @Test
+ public void canForgetNetwork_shouldInvisibleIfWithoutConfiguration() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+ mController = newWifiDetailPreferenceController2();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(false);
+ }
+
+ @Test
+ public void canForgetNetwork_ephemeral() {
+ setUpForConnectedNetwork();
+ when(mMockWifiInfo.isEphemeral()).thenReturn(true);
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(true);
+ }
+
+ @Test
+ public void canForgetNetwork_saved() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(true);
+ }
+
+ @Test
+ public void canForgetNetwork_lockedDown() {
+ setUpForConnectedNetwork();
+ lockDownNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton1Visible(false);
+ }
+
+ @Test
+ public void canShareNetwork_shouldInvisibleIfWithoutConfiguration() {
+ setUpForConnectedNetwork();
+ when(mMockAccessPoint.getConfig()).thenReturn(null);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton4Visible(false);
+ }
+
+ @Test
+ public void canModifyNetwork_saved() {
+ setUpForConnectedNetwork();
+ assertThat(mController.canModifyNetwork()).isTrue();
+ }
+
+ @Test
+ public void canModifyNetwork_lockedDown() {
+ setUpForConnectedNetwork();
+ lockDownNetwork();
+
+ assertThat(mController.canModifyNetwork()).isFalse();
+ }
+
+ /**
+ * Pretends that current network is locked down by device owner.
+ */
+ private void lockDownNetwork() {
+ final int doUserId = 123;
+ final int doUid = 1234;
+ String doPackage = "some.package";
+
+ mMockWifiConfig.creatorUid = doUid;
+ ComponentName doComponent = new ComponentName(doPackage, "some.Class");
+ try {
+ when(mMockPackageManager.getPackageUidAsUser(Matchers.anyString(), Matchers.anyInt()))
+ .thenReturn(doUid);
+ } catch (PackageManager.NameNotFoundException e) {
+ //do nothing
+ }
+ ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser(doComponent);
+ ShadowDevicePolicyManager.getShadow().setDeviceOwnerUserId(doUserId);
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 1);
+ }
+
+ @Test
+ public void forgetNetwork_ephemeral() {
+ setUpForConnectedNetwork();
+ String ssid = "ssid";
+ when(mMockWifiInfo.isEphemeral()).thenReturn(true);
+ when(mMockWifiInfo.getSSID()).thenReturn(ssid);
+
+ displayAndResume();
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager).disableEphemeralNetwork(ssid);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ }
+
+ @Test
+ public void forgetNetwork_saved() {
+ setUpForConnectedNetwork();
+ mMockWifiConfig.networkId = 5;
+
+ mController.displayPreference(mMockScreen);
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager).forget(mMockWifiConfig.networkId, null);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ }
+
+ @Test
+ public void forgetNetwork_shouldShowDialog() {
+ setUpForConnectedNetwork();
+ final WifiDetailPreferenceController2 spyController = spy(mController);
+
+ mMockWifiConfig.networkId = 5;
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.getPasspointFqdn()).thenReturn(FQDN);
+ spyController.displayPreference(mMockScreen);
+
+ mForgetClickListener.getValue().onClick(null);
+
+ verify(mMockWifiManager, times(0)).removePasspointConfiguration(FQDN);
+ verify(mMockMetricsFeatureProvider, times(0))
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
+ verify(spyController).showConfirmForgetDialog();
+ }
+
+ @Test
+ public void networkStateChangedIntent_shouldRefetchInfo() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void networkStateChangedIntent_shouldRefetchInfoForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void rssiChangedIntent_shouldRefetchInfo() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void rssiChangedIntent_shouldRefetchInfoForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ verify(mMockConnectivityManager, times(1)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(1)).getConnectionInfo();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockConnectivityManager, times(2)).getNetworkInfo(any(Network.class));
+ verify(mMockWifiManager, times(2)).getConnectionInfo();
+ }
+
+ @Test
+ public void networkDisconnectedState_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ when(mMockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(null);
+ mContext.sendBroadcast(new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void networkOnLost_shouldNotFinishActivityForConnectedNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ mCallbackCaptor.getValue().onLost(mMockNetwork);
+
+ verify(mMockActivity, never()).finish();
+ }
+
+ @Test
+ public void ipv6AddressPref_shouldHaveHostAddressTextSet() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV6_LINKLOCAL);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL1);
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
+
+ displayAndResume();
+
+ String expectedAddresses = String.join("\n",
+ asString(Constants.IPV6_LINKLOCAL),
+ asString(Constants.IPV6_GLOBAL1),
+ asString(Constants.IPV6_GLOBAL2));
+
+ verify(mMockIpv6AddressesPref).setSummary(expectedAddresses);
+ }
+
+ @Test
+ public void ipv6AddressPref_shouldNotBeSelectable() {
+ setUpForConnectedNetwork();
+ mLinkProperties.addLinkAddress(Constants.IPV6_GLOBAL2);
+
+ displayAndResume();
+
+ assertThat(mMockIpv6AddressesPref.isSelectable()).isFalse();
+ }
+
+ @Test
+ public void captivePortal_shouldShowSignInButton() {
+ setUpForConnectedNetwork();
+
+ InOrder inOrder = inOrder(mMockButtonsPref);
+
+ displayAndResume();
+
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(true);
+
+ nc.removeCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ updateNetworkCapabilities(nc);
+ inOrder.verify(mMockButtonsPref).setButton2Visible(false);
+ }
+
+ @Test
+ public void testSignInButton_shouldStartCaptivePortalApp() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ ArgumentCaptor<OnClickListener> captor = ArgumentCaptor.forClass(OnClickListener.class);
+ verify(mMockButtonsPref).setButton2OnClickListener(captor.capture());
+ captor.getValue().onClick(null);
+ verify(mMockConnectivityManager).startCaptivePortalApp(mMockNetwork);
+ verify(mMockMetricsFeatureProvider)
+ .action(mMockActivity, MetricsProto.MetricsEvent.ACTION_WIFI_SIGNIN);
+ }
+
+ @Test
+ public void testSignInButton_shouldHideSignInButtonForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ NetworkCapabilities nc = makeNetworkCapabilities();
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ when(mMockConnectivityManager.getNetworkCapabilities(mMockNetwork))
+ .thenReturn(new NetworkCapabilities(nc));
+
+ // verify onResume
+ displayAndResume();
+
+ verify(mMockButtonsPref, never()).setButton2Visible(true);
+ verify(mMockButtonsPref).setButton2Visible(false);
+
+ // verify onCapabilitiesChanged
+ updateNetworkCapabilities(nc);
+
+ verify(mMockButtonsPref, never()).setButton2Visible(true);
+ verify(mMockButtonsPref).setButton2Visible(false);
+ }
+
+ @Test
+ public void testConnectButton_shouldInvisibleForConnectNetwork() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ }
+
+ @Test
+ public void testConnectButton_shouldVisibleForDisconnectNetwork() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ }
+
+ private void setUpForToast() {
+ Resources res = mContext.getResources();
+ when(mMockActivity.getResources()).thenReturn(res);
+ }
+
+ @Test
+ public void testConnectButton_clickConnect_displayAsSuccess() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ mController.updateAccessPoint();
+
+ // check connect button invisible, be init as default state and toast success message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_connected_to_message, label));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButFailed_displayFailMessage() {
+ setUpForDisconnectedNetwork();
+ ArgumentCaptor<WifiManager.ActionListener> connectListenerCaptor =
+ ArgumentCaptor.forClass(WifiManager.ActionListener.class);
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), connectListenerCaptor.capture());
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ connectListenerCaptor.getValue().onFailure(-1);
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ private void verifyConnectBtnSetUpAsVisible(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ }
+
+ private void verifyConnectBtnSetUpAsConnecting(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connecting);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Enabled(false);
+ }
+
+ private void verifyConnectBtnBeInitAsDefault(InOrder inOrder) {
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Enabled(true);
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButTimeout_displayFailMessage() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectButTimeout_displayNotInRangeMessage() {
+ setUpForNotInRangeNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_not_in_range_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_displaySuccessMessage() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ String label = "title";
+ when(mMockAccessPoint.getTitle()).thenReturn(label);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ mController.updateAccessPoint();
+
+ // check connect button invisible, be init as default state and toast success message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(false);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_connected_to_message, label));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWiFi() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void
+ testConnectButton_clickConnectWhenWiFiDisabled_failedToConnectWifiBecauseNotInRange() {
+ setUpForNotInRangeNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify Wi-Fi enabled
+ mController.mWifiListener.onWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
+
+ // check had connect network and icon display as expected
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // update as failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_not_in_range_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectWhenWiFiDisabled_failedToEnableWifi() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(false); // wifi disabled
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check turn on Wi-Fi, display button as connecting and toast turn on Wi-Fi message
+ verify(mMockWifiManager, times(1)).setWifiEnabled(true);
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_turned_on_message));
+
+ // notify turn on Wi-Fi failed
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ verifyConnectBtnBeInitAsDefault(inOrder);
+ inOrder.verify(mMockButtonsPref, times(1)).setButton3Visible(true);
+ assertThat(ShadowToast.getTextOfLatestToast()).isEqualTo(
+ mContext.getString(R.string.wifi_failed_connect_message));
+ }
+
+ @Test
+ public void testConnectButton_clickConnectAndBackKey_ignoreTimeoutEvent() {
+ setUpForDisconnectedNetwork();
+ when(mMockWifiManager.isWifiEnabled()).thenReturn(true);
+ InOrder inOrder = inOrder(mMockButtonsPref);
+ setUpForToast();
+
+ displayAndResume();
+
+ // check connect button exist
+ verifyConnectBtnSetUpAsVisible(inOrder);
+
+ // click connect button
+ mController.connectNetwork();
+
+ // check display button as connecting
+ verify(mMockWifiManager, times(1)).connect(anyInt(), any(WifiManager.ActionListener.class));
+ verifyConnectBtnSetUpAsConnecting(inOrder);
+
+ // leave detail page
+ when(mMockFragment.getActivity()).thenReturn(null);
+
+ // timeout happened
+ mController.sTimer.onFinish();
+
+ // check connect button visible, be init as default and toast failed message
+ inOrder.verify(mMockButtonsPref, never()).setButton3Text(R.string.wifi_connect);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Icon(R.drawable.ic_settings_wireless);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Enabled(true);
+ inOrder.verify(mMockButtonsPref, never()).setButton3Visible(true);
+ assertThat(ShadowToast.shownToastCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void updateAccessPoint_returnFalseForNothingChanged() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isFalse();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForSignalLevelChanged() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // Level changed
+ when(mMockAccessPoint.getLevel()).thenReturn(LEVEL + 1);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsNotInRange() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // change as not in range
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(false);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsInRange() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ // change as in range
+ when(mMockAccessPoint.matches(any(AccessPoint.class))).thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsConnected() {
+ setUpForDisconnectedNetwork();
+
+ displayAndResume();
+
+ // change as connected
+ when(mMockAccessPoint.isActive()).thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForChangeAsDisconnected() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ // change as disconnected
+ when(mMockAccessPoint.isActive()).thenReturn(false);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void updateAccessPoint_returnTrueForAccessPointUpdated() {
+ setUpForConnectedNetwork();
+
+ displayAndResume();
+
+ // change as disconnected
+ when(mMockAccessPoint.update(mMockWifiConfig, mMockWifiInfo, mMockNetworkInfo))
+ .thenReturn(true);
+ boolean changed = mController.updateAccessPoint();
+
+ assertThat(changed).isTrue();
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldNotUpdateIfLevelIsSameForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockAccessPoint, times(3)).getLevel();
+ verify(mMockIconInjector, times(1)).getIcon(anyInt());
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldUpdateOnLevelChangeForConnectedNetwork() {
+ setUpForConnectedNetwork();
+ displayAndResume();
+
+ when(mMockAccessPoint.getLevel()).thenReturn(0);
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockAccessPoint, times(4)).getLevel();
+ verify(mMockIconInjector, times(2)).getIcon(anyInt());
+ }
+
+ @Test
+ public void testRefreshRssiViews_shouldNotUpdateForNotInRangeNetwork() {
+ setUpForNotInRangeNetwork();
+
+ displayAndResume();
+
+ when(mMockAccessPoint.getLevel()).thenReturn(0);
+ mContext.sendBroadcast(new Intent(WifiManager.RSSI_CHANGED_ACTION));
+
+ verify(mMockSignalStrengthPref, times(2)).setVisible(false);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldEnlarge() {
+ setUpForConnectedNetwork();
+ ArgumentCaptor<BitmapDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(BitmapDrawable.class);
+ Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+
+ int expectedSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ BitmapDrawable icon = drawableCaptor.getValue();
+ assertThat(icon.getMinimumWidth()).isEqualTo(expectedSize);
+ assertThat(icon.getMinimumHeight()).isEqualTo(expectedSize);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldEnlargeForDisconnectedNetwork() {
+ setUpForDisconnectedNetwork();
+ ArgumentCaptor<BitmapDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(BitmapDrawable.class);
+ Drawable original = mContext.getDrawable(Utils.getWifiIconResource(LEVEL)).mutate();
+ when(mMockIconInjector.getIcon(anyInt())).thenReturn(original);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+
+ int expectedSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.wifi_detail_page_header_image_size);
+ BitmapDrawable icon = drawableCaptor.getValue();
+ assertThat(icon.getMinimumWidth()).isEqualTo(expectedSize);
+ assertThat(icon.getMinimumHeight()).isEqualTo(expectedSize);
+ }
+
+ @Test
+ public void testRedrawIconForHeader_shouldNotEnlargeIfNotVectorDrawable() {
+ setUpForConnectedNetwork();
+ ArgumentCaptor<ColorDrawable> drawableCaptor =
+ ArgumentCaptor.forClass(ColorDrawable.class);
+
+ displayAndResume();
+
+ verify(mMockHeaderController, times(1)).setIcon(drawableCaptor.capture());
+ ColorDrawable icon = drawableCaptor.getValue();
+ assertThat(icon).isNotNull();
+ }
+
+ @Test
+ public void checkMacTitle_whenPrivacyRandomizedMac_shouldBeRandom() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockMacAddress.toString()).thenReturn(RANDOMIZED_MAC_ADDRESS);
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setTitle(R.string.wifi_advanced_randomized_mac_address_title);
+ }
+
+ @Test
+ public void checkMacTitle_whenPrivacyDeviceMac_shouldBeFactory() {
+ setUpForDisconnectedNetwork();
+ mMockWifiConfig.macRandomizationSetting = WifiConfiguration.RANDOMIZATION_NONE;
+ when(mMockWifiConfig.getRandomizedMacAddress()).thenReturn(mMockMacAddress);
+ when(mMockWifiManager.getFactoryMacAddresses())
+ .thenReturn(new String[]{FACTORY_MAC_ADDRESS});
+
+ displayAndResume();
+
+ verify(mMockMacAddressPref).setTitle(R.string.wifi_advanced_device_mac_address_title);
+ }
+
+ @Test
+ @Ignore
+ public void entityHeader_expiredPasspointR1_shouldHandleExpiration() {
+ setUpForDisconnectedNetwork();
+ when(mMockAccessPoint.isPasspoint()).thenReturn(true);
+ when(mMockAccessPoint.isPasspointConfigurationR1()).thenReturn(true);
+ when(mMockAccessPoint.isExpired()).thenReturn(true);
+ String expireSummary = mContext.getResources().getString(
+ com.android.settingslib.R.string.wifi_passpoint_expired);
+
+ displayAndResume();
+
+ verify(mMockButtonsPref).setButton3Visible(false);
+ verify(mMockHeaderController).setSummary(expireSummary);
+ }
+
+ private ActionButtonsPreference createMock() {
+ final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
+ when(pref.setButton1Text(anyInt())).thenReturn(pref);
+ when(pref.setButton1Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton2Text(anyInt())).thenReturn(pref);
+ when(pref.setButton2Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton3Text(anyInt())).thenReturn(pref);
+ when(pref.setButton3Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton3Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton3OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ when(pref.setButton4Text(anyInt())).thenReturn(pref);
+ when(pref.setButton4Icon(anyInt())).thenReturn(pref);
+ when(pref.setButton4Enabled(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4Visible(anyBoolean())).thenReturn(pref);
+ when(pref.setButton4OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
+
+ return pref;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
new file mode 100644
index 0000000..517c96a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiMeteredPreferenceController2Test.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+
+import androidx.preference.DropDownPreference;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiMeteredPreferenceController2Test {
+
+ private static final int METERED_OVERRIDE_NONE = 0;
+ private static final int METERED_OVERRIDE_METERED = 1;
+ private static final int METERED_OVERRIDE_NOT_METERED = 2;
+
+ @Mock
+ private WifiConfiguration mWifiConfiguration;
+
+ private WifiMeteredPreferenceController2 mPreferenceController;
+ private Context mContext;
+ private DropDownPreference mDropDownPreference;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+
+ mPreferenceController = spy(
+ new WifiMeteredPreferenceController2(mContext, mWifiConfiguration));
+ mDropDownPreference = new DropDownPreference(mContext);
+ mDropDownPreference.setEntries(R.array.wifi_metered_entries);
+ mDropDownPreference.setEntryValues(R.array.wifi_metered_values);
+ }
+
+ @Test
+ public void testUpdateState_wifiMetered_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_METERED).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as metered");
+ }
+
+ @Test
+ public void testUpdateState_wifiNotMetered_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_NOT_METERED).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Treat as unmetered");
+ }
+
+ @Test
+ public void testUpdateState_wifiAuto_setCorrectValue() {
+ doReturn(METERED_OVERRIDE_NONE).when(mPreferenceController).getMeteredOverride();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.getEntry()).isEqualTo("Detect automatically");
+ }
+
+ @Test
+ public void testController_resilientToNullConfig() {
+ mPreferenceController = spy(new WifiMeteredPreferenceController2(mContext, null));
+
+ mPreferenceController.getMeteredOverride();
+ mPreferenceController.onPreferenceChange(mDropDownPreference, 1);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java
new file mode 100644
index 0000000..91cc01e
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiPrivacyPreferenceController2Test.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.details2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+
+import androidx.preference.DropDownPreference;
+
+import com.android.settings.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class WifiPrivacyPreferenceController2Test {
+
+ private static final int PRIVACY_RANDOMIZED = WifiConfiguration.RANDOMIZATION_PERSISTENT;
+ private static final int PRIVACY_TRUSTED = WifiConfiguration.RANDOMIZATION_NONE;
+
+ @Mock
+ private WifiConfiguration mWifiConfiguration;
+
+ private WifiPrivacyPreferenceController2 mPreferenceController;
+ private Context mContext;
+ private DropDownPreference mDropDownPreference;
+ private String[] mPerferenceStrings;
+
+ @Before
+ public void setUp() {
+ mContext = RuntimeEnvironment.application;
+
+ WifiPrivacyPreferenceController2 preferenceController =
+ new WifiPrivacyPreferenceController2(mContext);
+ preferenceController.setWifiConfiguration(mWifiConfiguration);
+ mPreferenceController = spy(preferenceController);
+ mDropDownPreference = new DropDownPreference(mContext);
+ mDropDownPreference.setEntries(R.array.wifi_privacy_entries);
+ mDropDownPreference.setEntryValues(R.array.wifi_privacy_values);
+
+ mPerferenceStrings = mContext.getResources().getStringArray(R.array.wifi_privacy_entries);
+ }
+
+ @Test
+ public void testUpdateState_wifiPrivacy_setCorrectValue() {
+ doReturn(PRIVACY_TRUSTED).when(mPreferenceController).getRandomizationValue();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ int prefValue = mPreferenceController.translateMacRandomizedValueToPrefValue(
+ PRIVACY_TRUSTED);
+ assertThat(mDropDownPreference.getEntry()).isEqualTo(mPerferenceStrings[prefValue]);
+ }
+
+ @Test
+ public void testUpdateState_wifiNotMetered_setCorrectValue() {
+ doReturn(PRIVACY_RANDOMIZED).when(mPreferenceController).getRandomizationValue();
+
+ mPreferenceController.updateState(mDropDownPreference);
+
+ int prefValue = mPreferenceController.translateMacRandomizedValueToPrefValue(
+ PRIVACY_RANDOMIZED);
+ assertThat(mDropDownPreference.getEntry()).isEqualTo(mPerferenceStrings[prefValue]);
+ }
+
+ @Test
+ public void testController_resilientToNullConfig() {
+ mPreferenceController = spy(new WifiPrivacyPreferenceController2(mContext));
+
+ mPreferenceController.getRandomizationValue();
+ mPreferenceController.onPreferenceChange(mDropDownPreference, "1");
+ }
+
+ @Test
+ public void testUpdateState_isNotEphemeralNetwork_shouldBeSelectable() {
+ mPreferenceController.setIsEphemeral(false);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateState_isEphemeralNetwork_shouldNotSelectable() {
+ mPreferenceController.setIsEphemeral(true);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isFalse();
+ }
+
+ @Test
+ public void testUpdateState_isNotPasspointNetwork_shouldBeSelectable() {
+ mPreferenceController.setIsPasspoint(false);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isTrue();
+ }
+
+ @Test
+ public void testUpdateState_isPasspointNetwork_shouldNotSelectable() {
+ mPreferenceController.setIsPasspoint(true);
+ mPreferenceController.updateState(mDropDownPreference);
+
+ assertThat(mDropDownPreference.isSelectable()).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java
new file mode 100644
index 0000000..f50021b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsPreferenceController2Test.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import static com.android.settings.core.BasePreferenceController.AVAILABLE;
+import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settingslib.wifi.WifiEntryPreference;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class SavedAccessPointsPreferenceController2Test {
+
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+ @Mock
+ private PreferenceCategory mPreferenceCategory;
+
+ private Context mContext;
+ private SavedAccessPointsWifiSettings2 mSettings;
+ private SavedAccessPointsPreferenceController2 mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mSettings = spy(new SavedAccessPointsWifiSettings2());
+ mController = spy(new SavedAccessPointsPreferenceController2(mContext, "test_key"));
+ mController.setHost(mSettings);
+
+ when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
+ .thenReturn(mPreferenceCategory);
+ when(mPreferenceCategory.getContext()).thenReturn(mContext);
+ }
+
+ @Test
+ public void getAvailability_noSavedAccessPoint_shouldNotAvailable() {
+ mController.mWifiEntries = new ArrayList<>();
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ @Test
+ public void getAvailability_oneSavedAccessPoint_shouldAvailable() {
+ final WifiEntry mockWifiEntry = mock(WifiEntry.class);
+ mController.mWifiEntries = Arrays.asList(mockWifiEntry);
+
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void displayPreference_oneAccessPoint_shouldListIt() {
+ final String title = "ssid_title";
+ final WifiEntry mockWifiEntry = mock(WifiEntry.class);
+ when(mockWifiEntry.getTitle()).thenReturn(title);
+ final ArgumentCaptor<WifiEntryPreference> captor =
+ ArgumentCaptor.forClass(WifiEntryPreference.class);
+
+ mController.displayPreference(mPreferenceScreen, Arrays.asList(mockWifiEntry));
+
+ verify(mPreferenceCategory).addPreference(captor.capture());
+
+ final List<WifiEntryPreference> prefs = captor.getAllValues();
+ assertThat(prefs.size()).isEqualTo(1);
+ assertThat(prefs.get(0).getTitle()).isEqualTo(title);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
new file mode 100644
index 0000000..ab1b51c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/wifi/savedaccesspoints2/SavedAccessPointsWifiSettings2Test.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.savedaccesspoints2;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+@RunWith(RobolectricTestRunner.class)
+public class SavedAccessPointsWifiSettings2Test {
+
+ @Mock
+ private SubscribedAccessPointsPreferenceController2 mSubscribedApController;
+ @Mock
+ private SavedAccessPointsPreferenceController2 mSavedApController;
+
+ private TestFragment mSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSettings = spy(new TestFragment());
+
+ doReturn(mSubscribedApController).when(mSettings)
+ .use(SubscribedAccessPointsPreferenceController2.class);
+ doReturn(mSavedApController).when(mSettings)
+ .use(SavedAccessPointsPreferenceController2.class);
+ }
+
+ @Test
+ public void verifyConstants() {
+ assertThat(mSettings.getMetricsCategory()).isEqualTo(MetricsEvent.WIFI_SAVED_ACCESS_POINTS);
+ assertThat(mSettings.getPreferenceScreenResId())
+ .isEqualTo(R.xml.wifi_display_saved_access_points2);
+ }
+
+ public static class TestFragment extends SavedAccessPointsWifiSettings2 {
+
+ public <T extends AbstractPreferenceController> T use(Class<T> clazz) {
+ return super.use(clazz);
+ }
+ }
+}