Merge "Import translations. DO NOT MERGE ANYWHERE" into udc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 433d8b6..6aad2af 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1358,7 +1358,7 @@
<activity
android:name="Settings$SmartAutoRotateSettingsActivity"
android:label="@string/accelerometer_title"
- android:icon="@drawable/ic_screen_rotation_24dp"
+ android:icon="@drawable/ic_screen_rotation"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.settings.AUTO_ROTATE_SETTINGS" />
@@ -2955,13 +2955,6 @@
</intent-filter>
</activity>
- <activity android:name=".bluetooth.RequestPermissionHelperActivity"
- android:label="@string/bluetooth_pairing_request"
- android:excludeFromRecents="true"
- android:permission="android.permission.BLUETOOTH_CONNECT"
- android:theme="@*android:style/Theme.DeviceDefault.Dialog.Alert.DayNight">
- </activity>
-
<service android:name=".bluetooth.BluetoothPairingService" />
<receiver android:name=".bluetooth.BluetoothPairingRequest"
@@ -4826,6 +4819,22 @@
</receiver>
<activity
+ android:name=".notetask.shortcut.CreateNoteTaskShortcutActivity"
+ android:enabled="false"
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:resizeableActivity="false"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:label="@string/note_task_button_label"
+ android:icon="@drawable/ic_note_task_shortcut_widget">
+
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="com.android.settings.bluetooth.QrCodeScanModeActivity"
android:permission="android.permission.BLUETOOTH_CONNECT"
android:exported="true">
diff --git a/OWNERS b/OWNERS
index 8938b64..6d4f95f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -2,22 +2,18 @@
android-settings-core-eng+gerrit@google.com
# People who can approve changes for submission
-arcwang@google.com
cantol@google.com
chaohuiw@google.com
chiujason@google.com
-cyl@google.com
+dswliu@google.com
edgarwang@google.com
-llz@google.com
millchen@google.com
songchenxi@google.com
-stanleytfwang@google.com
sunnyshao@google.com
yantingyang@google.com
ykhung@google.com
# Emergency only
-lijun@google.com
wangqi@google.com
yanglu@google.com
diff --git a/color-check-baseline.xml b/color-check-baseline.xml
index 2fd63fc..509d9ae 100644
--- a/color-check-baseline.xml
+++ b/color-check-baseline.xml
@@ -4437,6 +4437,22 @@
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
+ errorLine1=" android:color="@color/homepage_display_background" />"
+ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+ <location
+ file="res/drawable/ic_screen_rotation.xml"
+ line="24"
+ column="13"/>
+ </issue>
+
+ <issue
+ id="HardCodedColor"
+ severity="Error"
+ message="Avoid using hardcoded color"
+ category="Correctness"
+ priority="4"
+ summary="Using hardcoded color"
+ explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.
This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" <background android:drawable="@color/shortcut_background"/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
diff --git a/res-product/values/strings.xml b/res-product/values/strings.xml
index 2f01c47..2a58f45 100644
--- a/res-product/values/strings.xml
+++ b/res-product/values/strings.xml
@@ -96,6 +96,12 @@
<!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
<string name="security_settings_face_enroll_introduction_consent_message_0" product="device">Allow your child to use their face to unlock their device</string>
<!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
+ <string name="security_settings_face_enroll_introduction_consent_message_0_class3" product="default">Allow your child to use their face to unlock their phone or verify it\u2019s them. This happens when they sign in to apps, approve a purchase, and more.</string>
+ <!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
+ <string name="security_settings_face_enroll_introduction_consent_message_0_class3" product="tablet">Allow your child to use their face to unlock their tablet or verify it\u2019s them. This happens when they sign in to apps, approve a purchase, and more.</string>
+ <!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
+ <string name="security_settings_face_enroll_introduction_consent_message_0_class3" product="device">Allow your child to use their face to unlock their device or verify it\u2019s them. This happens when they sign in to apps, approve a purchase, and more.</string>
+ <!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
<string name="security_settings_face_enroll_introduction_consent_message" product="default">Using your child\u2019s face to unlock their phone may be less secure than a strong pattern or PIN.</string>
<!-- Introduction detail message shown in face enrollment dialog when asking for parental consent [CHAR LIMIT=NONE]-->
<string name="security_settings_face_enroll_introduction_consent_message" product="tablet">Using your child\u2019s face to unlock their tablet may be less secure than a strong pattern or PIN.</string>
diff --git a/res/drawable/ic_check_24dp.xml b/res/drawable/ic_check_24dp.xml
index 9ac8021..0ed6b32 100644
--- a/res/drawable/ic_check_24dp.xml
+++ b/res/drawable/ic_check_24dp.xml
@@ -18,8 +18,8 @@
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?androidprv:attr/materialColorPrimaryContainer">
+ android:tint="?androidprv:attr/colorAccent">
<path
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"
android:fillColor="@android:color/white"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/res/drawable/ic_note_task_shortcut_widget.xml b/res/drawable/ic_note_task_shortcut_widget.xml
new file mode 100644
index 0000000..e37c22a
--- /dev/null
+++ b/res/drawable/ic_note_task_shortcut_widget.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillAlpha="1"
+ android:fillColor="#636C6F"
+ android:fillType="nonZero"
+ android:pathData="M17.6258,4.96L19.0358,6.37L7.4058,18.01L5.9958,16.6L17.6258,4.96ZM16.1358,3.62L4.1258,15.63L3.0158,19.83C2.9058,20.45 3.3858,21 3.9958,21C4.0558,21 4.1058,21 4.1658,20.99L8.3658,19.88L20.3758,7.86C20.7758,7.46 20.9958,6.93 20.9958,6.37C20.9958,5.81 20.7758,5.28 20.3758,4.88L19.1058,3.61C18.7158,3.22 18.1858,3 17.6258,3C17.0658,3 16.5358,3.22 16.1358,3.62Z" />
+ <path
+ android:fillAlpha="1"
+ android:fillColor="#636C6F"
+ android:fillType="nonZero"
+ android:pathData="M20.1936,15.3369C20.3748,16.3837 19.9151,17.5414 18.8846,18.7597C19.1546,18.872 19.4576,18.9452 19.7724,18.9867C20.0839,19.0278 20.3683,19.0325 20.5749,19.0266C20.6772,19.0236 20.7578,19.0181 20.8101,19.0138C20.8362,19.0116 20.855,19.0097 20.8657,19.0085L20.8754,19.0074L20.875,19.0075C21.4217,18.9385 21.9214,19.325 21.9918,19.8718C22.0624,20.4195 21.6756,20.9208 21.1279,20.9914L21,19.9996C21.1279,20.9914 21.1265,20.9916 21.1265,20.9916L21.1249,20.9918L21.1211,20.9923L21.1107,20.9935L21.0795,20.997C21.0542,20.9998 21.0199,21.0032 20.9775,21.0067C20.8929,21.0138 20.7753,21.0216 20.6323,21.0257C20.3481,21.0339 19.9533,21.0279 19.5109,20.9695C18.873,20.8854 18.0393,20.6793 17.3106,20.1662C16.9605,20.3559 16.5876,20.4952 16.2299,20.6003C15.5742,20.7927 14.8754,20.8968 14.2534,20.9534C13.6801,21.0055 13.4553,21.0037 13.1015,21.0008C13.0689,21.0005 13.0352,21.0002 13,21H12.8594C12.8214,21.0002 12.785,21.0006 12.7504,21.0009C12.6524,21.0019 12.5683,21.0027 12.5,21H12.0562C12.0277,21.0003 12.0054,21.0006 11.9926,21.001L11.9751,21H9L11,19H11.9795C11.9929,18.9997 12.0064,18.9997 12.0199,19H12.4117C12.4534,18.9996 12.4864,18.9995 12.5,19H12.9675C12.977,18.9999 12.9878,18.9999 13,19C13.0446,19.0003 13.0859,19.0007 13.1249,19.0011C13.4259,19.0038 13.591,19.0054 14.0723,18.9616C14.6201,18.9118 15.1795,18.8242 15.6665,18.6813C15.753,18.6559 15.8346,18.6295 15.9114,18.6022C15.0315,17.2981 14.7125,16.1044 15.015,15.0829C15.4095,13.7511 16.6784,13.2418 17.7026,13.2864C18.7262,13.3309 19.954,13.9529 20.1936,15.3369ZM16.9327,15.6508C16.873,15.8523 16.8651,16.3878 17.4697,17.334C18.2007,16.4284 18.2585,15.8839 18.2229,15.6781C18.1939,15.5108 18.0297,15.3025 17.6157,15.2845C17.2025,15.2665 16.9885,15.4626 16.9327,15.6508Z" />
+</vector>
diff --git a/res/drawable/ic_screen_rotation.xml b/res/drawable/ic_screen_rotation.xml
new file mode 100644
index 0000000..f1bde27
--- /dev/null
+++ b/res/drawable/ic_screen_rotation.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2023 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.
+ -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <com.android.settingslib.widget.AdaptiveIconShapeDrawable
+ android:width="@dimen/dashboard_tile_image_size"
+ android:height="@dimen/dashboard_tile_image_size"
+ android:color="@color/homepage_display_background" />
+ </item>
+
+ <item
+ android:width="@dimen/dashboard_tile_foreground_image_size"
+ android:height="@dimen/dashboard_tile_foreground_image_size"
+ android:start="@dimen/dashboard_tile_foreground_image_inset"
+ android:top="@dimen/dashboard_tile_foreground_image_inset"
+ android:drawable="@drawable/ic_screen_rotation_24dp" />
+</layer-list>
diff --git a/res/layout/locale_dialog.xml b/res/layout/locale_dialog.xml
index cbdb37e..480c0e2 100644
--- a/res/layout/locale_dialog.xml
+++ b/res/layout/locale_dialog.xml
@@ -21,7 +21,9 @@
android:paddingStart="@dimen/admin_details_dialog_padding"
android:paddingEnd="@dimen/admin_details_dialog_padding"
android:paddingBottom="@dimen/admin_details_dialog_padding_bottom"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:layoutDirection="locale"
+ android:textDirection="locale">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -41,7 +43,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_horizontal"
- android:textAppearance="@style/TextAppearance.AdminDialogTitle"/>
+ android:textAppearance="@style/TextAppearance.AdminDialogTitle"
+ android:textAlignment="textStart"/>
</LinearLayout>
<ScrollView
@@ -51,15 +54,16 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/dialog_msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLength="200"
- android:gravity="left"
android:autoLink="email|phone|web"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textColor="?android:attr/textColorSecondary"
+ android:textAlignment="textStart"/>
</LinearLayout>
</ScrollView>
</LinearLayout>
diff --git a/res/layout/modifier_key_picker_dialog.xml b/res/layout/modifier_key_picker_dialog.xml
index 6de3294..fd4d75e 100644
--- a/res/layout/modifier_key_picker_dialog.xml
+++ b/res/layout/modifier_key_picker_dialog.xml
@@ -14,94 +14,102 @@
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:id="@+id/modifier_key_fragment_container"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:id="@+id/modifier_key_fragment_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="?android:dividerHorizontal"
- android:showDividers="end" >
- <TextView
- android:id="@+id/modifier_key_picker_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="24dip"
- android:layout_marginBottom="8dip"
- android:layout_gravity="center_horizontal"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:textColor="?android:attr/textColorPrimary"
- android:text="@string/modifier_keys_picker_title"/>
+ <LinearLayout
+ android:id="@+id/modifier_key_fragment_container"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
- <TextView
- android:id="@+id/modifier_key_picker_summary"
- android:layout_width="wrap_content"
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:dividerHorizontal"
+ android:showDividers="end" >
+ <TextView
+ android:id="@+id/modifier_key_picker_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="24dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/modifier_keys_picker_title"/>
+
+ <TextView
+ android:id="@+id/modifier_key_picker_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_gravity="center_horizontal"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+ </LinearLayout>
+
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:divider="?android:dividerHorizontal"
+ android:showDividers="end">
+ <ListView
+ android:id="@+id/modifier_key_picker"
+ android:layout_width="wrap_content"
+ android:layout_height="352dp"
+ android:divider="@null"
+ android:dividerHeight="8dp"
+ android:padding="16dp"
+ android:listSelector="@drawable/modifier_key_lisetview_background"
+ />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:layout_marginBottom="16dip"
- android:layout_gravity="center_horizontal"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp">
+
+ <Button
+ android:id="@+id/modifier_key_cancel_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp"
+ android:layout_marginStart="8dp"
+ android:layout_alignParentStart="true"
+ android:paddingVertical="14dp"
+ android:drawablePadding="9dp"
+ style="@style/ModifierKeyButtonCancel"
+ android:textColor="?android:attr/textColorPrimary"
+ android:text="@string/modifier_keys_cancel"/>
+
+ <Button
+ android:id="@+id/modifier_key_done_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="6dp"
+ android:layout_marginEnd="8dp"
+ android:layout_alignParentEnd="true"
+ android:paddingVertical="14dp"
+ android:drawablePadding="9dp"
+ style="@style/ModifierKeyButtonDone"
+ android:textColor="?androidprv:attr/materialColorOnPrimary"
+ android:text="@string/modifier_keys_done"/>
+
+ </RelativeLayout>
</LinearLayout>
-
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:divider="?android:dividerHorizontal"
- android:showDividers="end">
- <ListView
- android:id="@+id/modifier_key_picker"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:divider="@null"
- android:dividerHeight="8dp"
- android:padding="16dip"
- android:listSelector="@drawable/modifier_key_lisetview_background"
- />
- </LinearLayout>
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="8dip"
- android:layout_marginEnd="8dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1">
-
- <Button
- android:id="@+id/modifier_key_cancel_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_marginStart="8dip"
- android:layout_alignParentStart="true"
- android:paddingVertical="14dp"
- android:drawablePadding="9dp"
- style="@style/ModifierKeyButtonCancel"
- android:textColor="?android:attr/textColorPrimary"
- android:text="@string/modifier_keys_cancel"/>
-
- <Button
- android:id="@+id/modifier_key_done_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_marginEnd="8dip"
- android:layout_alignParentEnd="true"
- android:paddingVertical="14dp"
- android:drawablePadding="9dp"
- style="@style/ModifierKeyButtonDone"
- android:textColor="?androidprv:attr/materialColorOnPrimary"
- android:text="@string/modifier_keys_done"/>
-
- </RelativeLayout>
-</LinearLayout>
\ No newline at end of file
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout/modifier_key_reset_dialog.xml b/res/layout/modifier_key_reset_dialog.xml
index 11712bf..784b608 100644
--- a/res/layout/modifier_key_reset_dialog.xml
+++ b/res/layout/modifier_key_reset_dialog.xml
@@ -26,7 +26,6 @@
android:layout_marginTop="24dip"
android:layout_marginBottom="8dip"
android:layout_gravity="center_horizontal"
- android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/modifier_keys_reset_title" />
@@ -39,7 +38,6 @@
android:layout_marginBottom="16dip"
android:layout_marginStart="32dip"
android:layout_marginEnd="32dip"
- android:gravity="center"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
diff --git a/res/values-bs/arrays.xml b/res/values-bs/arrays.xml
index db8b862..2a21b78 100644
--- a/res/values-bs/arrays.xml
+++ b/res/values-bs/arrays.xml
@@ -265,7 +265,7 @@
<item msgid="6749550886745567276">"Jačina zvuka zvona"</item>
<item msgid="2218685029915863168">"Jačina zvuka medija"</item>
<item msgid="4266577290496513640">"Jačina zvuka alarma"</item>
- <item msgid="8608084169623998854">"Jačina zvuka za obavještenja"</item>
+ <item msgid="8608084169623998854">"Jačina zvuka obavještenja"</item>
<item msgid="7948784184567841794">"Jačina zvuka za Bluetooth vezu"</item>
<item msgid="1148968792599973150">"Drži aktivnim"</item>
<item msgid="8482874682804856549">"Lokacija"</item>
diff --git a/res/values-hy/arrays.xml b/res/values-hy/arrays.xml
index 2bf9c67..fb38c9b 100644
--- a/res/values-hy/arrays.xml
+++ b/res/values-hy/arrays.xml
@@ -265,7 +265,7 @@
<item msgid="6749550886745567276">"Զանգի բարձրություն"</item>
<item msgid="2218685029915863168">"Մուլտիմեդիայի ձայնը"</item>
<item msgid="4266577290496513640">"Զարթուցիչի ձայնի բարձրություն"</item>
- <item msgid="8608084169623998854">"Ծանուցման ձայնի բարձրություն"</item>
+ <item msgid="8608084169623998854">"Ծանուցումների ձայնի ուժգնություն"</item>
<item msgid="7948784184567841794">"Bluetooth-ի ձայնի բարձրություն"</item>
<item msgid="1148968792599973150">"Արթուն պահել"</item>
<item msgid="8482874682804856549">"Տեղադրություն"</item>
diff --git a/res/values-ne/arrays.xml b/res/values-ne/arrays.xml
index 992bd5b..cd9aba0 100644
--- a/res/values-ne/arrays.xml
+++ b/res/values-ne/arrays.xml
@@ -198,7 +198,7 @@
<item msgid="6485000384018554920">"घन्टी मात्रा"</item>
<item msgid="3378000878531336372">"मिडियाको भोल्युम"</item>
<item msgid="5272927168355895681">"अलार्मको भोल्युम"</item>
- <item msgid="4422070755065530548">"सूचना मात्रा"</item>
+ <item msgid="4422070755065530548">"सूचनाको भोल्युम"</item>
<item msgid="3250654589277825306">"ब्लुटुथ मात्रा"</item>
<item msgid="4212187233638382465">"जागा रहनुहोस्"</item>
<item msgid="5099026183238335900">"स्थानको निरीक्षण गर्नुहोस्"</item>
diff --git a/res/values-or/arrays.xml b/res/values-or/arrays.xml
index e839050..8a319e5 100644
--- a/res/values-or/arrays.xml
+++ b/res/values-or/arrays.xml
@@ -196,8 +196,8 @@
<item msgid="617344340943430125">"ମାଷ୍ଟର୍ ଭଲ୍ୟୁମ୍"</item>
<item msgid="1249691739381713634">"ଭଏସ ଭଲ୍ୟୁମ"</item>
<item msgid="6485000384018554920">"ରିଙ୍ଗ ଭଲ୍ୟୁମ୍"</item>
- <item msgid="3378000878531336372">"ମିଡିଆ ଭଲ୍ୟୁମ୍"</item>
- <item msgid="5272927168355895681">"ଆଲାରାମର ଭଲ୍ୟୁମ୍"</item>
+ <item msgid="3378000878531336372">"ମିଡିଆ ଭଲ୍ୟୁମ"</item>
+ <item msgid="5272927168355895681">"ଆଲାରାମ ଭଲ୍ୟୁମ"</item>
<item msgid="4422070755065530548">"ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍"</item>
<item msgid="3250654589277825306">"ବ୍ଲୁଟୂଥ୍ ଭଲ୍ୟୁମ୍"</item>
<item msgid="4212187233638382465">"ଜାଗ୍ରତ ରଖନ୍ତୁ"</item>
@@ -263,7 +263,7 @@
<item msgid="745291221457314879">"ମାଷ୍ଟର୍ ଭଲ୍ୟୁମ୍"</item>
<item msgid="4722479281326245754">"ଭଏସ୍ ଭଲ୍ୟୁମ୍"</item>
<item msgid="6749550886745567276">"ରିଙ୍ଗ ଭଲ୍ୟୁମ୍"</item>
- <item msgid="2218685029915863168">"ମିଡିଆ ଭଲ୍ୟୁମ୍"</item>
+ <item msgid="2218685029915863168">"ମିଡିଆ ଭଲ୍ୟୁମ"</item>
<item msgid="4266577290496513640">"ଆଲାରାମ୍ ଭଲ୍ୟୁମ୍"</item>
<item msgid="8608084169623998854">"ବିଜ୍ଞପ୍ତି ଭଲ୍ୟୁମ୍"</item>
<item msgid="7948784184567841794">"ବ୍ଲୁଟୂଥ୍ ଭଲ୍ୟୁମ୍"</item>
diff --git a/res/values-pa/arrays.xml b/res/values-pa/arrays.xml
index a2742e6..7a799af 100644
--- a/res/values-pa/arrays.xml
+++ b/res/values-pa/arrays.xml
@@ -198,7 +198,7 @@
<item msgid="6485000384018554920">"ਰਿੰਗ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="3378000878531336372">"ਮੀਡੀਆ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="5272927168355895681">"ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼"</item>
- <item msgid="4422070755065530548">"ਸੂਚਨਾ ਵੌਲਿਊਮ"</item>
+ <item msgid="4422070755065530548">"ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="3250654589277825306">"bluetooth ਵੌਲਿਊਮ"</item>
<item msgid="4212187233638382465">"ਸਕਿਰਿਆ ਰੱਖੋ"</item>
<item msgid="5099026183238335900">"ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਦਾ ਨਿਰੀਖਣ ਕਰੋ"</item>
@@ -265,7 +265,7 @@
<item msgid="6749550886745567276">"ਰਿੰਗ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="2218685029915863168">"ਮੀਡੀਆ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="4266577290496513640">"ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼"</item>
- <item msgid="8608084169623998854">"ਸੂਚਨਾ ਵੌਲਿਊਮ"</item>
+ <item msgid="8608084169623998854">"ਸੂਚਨਾ ਦੀ ਅਵਾਜ਼"</item>
<item msgid="7948784184567841794">"Bluetooth ਵੌਲਿਊਮ"</item>
<item msgid="1148968792599973150">"ਸਕਿਰਿਆ ਰੱਖੋ"</item>
<item msgid="8482874682804856549">"ਟਿਕਾਣਾ"</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 844d8ce..bdf8000 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -427,6 +427,12 @@
<string name="friday_first_day_of_week">Friday</string>
<!-- The title of saturday for preference of first day of week. [CHAR LIMIT=50] -->
<string name="saturday_first_day_of_week">Saturday</string>
+ <!-- Title for regional preference footer. [CHAR LIMIT=NONE] -->
+ <string name="title_regional_pref_footer">If an app doesn’t support regional preferences, the app will use its default locale settings.</string>
+ <!-- Description for text in regional preference footer. [CHAR LIMIT=NONE] -->
+ <string name="desc_regional_pref_footer_learn_more">Learn more about language preferences.</string>
+ <!-- TODO(b/277573274): Update the learn more url in the regional preference. -->
+ <string name="regional_pref_footer_learn_more_link" translatable="false">https://support.google.com/android</string>
<!-- The title of the confirmation dialog shown when the user selects one / several languages and tries to remove them [CHAR LIMIT=60] -->
<string name="dlg_remove_locales_title">{count, plural,
@@ -2061,6 +2067,10 @@
<string name="wifi_hotspot_speed_summary_unavailable">Not available in your country or region</string>
<!-- The footer message for switch screen resolution [CHAR LIMIT=NONE] -->
<string name="wifi_hotspot_speed_footer">If your preferred frequency isn\u0027t available, your hotspot may use a different one. Hotspot security settings may change if you change the frequency.</string>
+ <!-- Summary for the Wi-Fi hotspot security unavailable [CHAR LIMIT=NONE] -->
+ <string name="wifi_hotspot_security_summary_unavailable">Not available with 6 GHz</string>
+ <!-- The footer message for Wi-Fi hotspot security settings [CHAR LIMIT=NONE] -->
+ <string name="wifi_hotspot_security_footer">Security settings may change if you change the hotspot’s frequency</string>
<!-- Summary text when turning hotspot on -->
<string name="wifi_tether_starting">Turning hotspot on\u2026</string>
@@ -6370,7 +6380,7 @@
<!-- Title of preference to enable calling and SMS [CHAR LIMIT=45] -->
<string name="user_enable_calling_sms">Turn on phone calls & SMS</string>
<!-- Title of preference to grant user admin privileges [CHAR LIMIT=45] -->
- <string name="user_grant_admin">Give this user admin privileges</string>
+ <string name="user_grant_admin">Make this user an admin</string>
<!-- Title of preference to remove the user [CHAR LIMIT=35] -->
<string name="user_remove_user">Delete user</string>
<!-- Title for confirmation of turning on calls and SMS [CHAR LIMIT=45] -->
@@ -6380,7 +6390,7 @@
<!-- Title for confirmation of revoking admin privileges [CHAR LIMIT=45] -->
<string name="user_revoke_admin_confirm_title">Remove admin privileges?</string>
<!-- Message for confirmation of revoking admin privileges [CHAR LIMIT=none] -->
- <string name="user_revoke_admin_confirm_message">Are you sure you want to remove this user\'s admin privileges?</string>
+ <string name="user_revoke_admin_confirm_message">If you remove admin privileges for this user, you or another admin can give them back later.</string>
<!-- Title for the emergency info preference [CHAR LIMIT=40] -->
<string name="emergency_info_title">Emergency information</string>
<!-- Summary for the emergency info preference [CHAR LIMIT=40] -->
@@ -11898,6 +11908,12 @@
<!-- Developer settings: Summary for disabling phantom process monitoring. [CHAR LIMIT=NONE]-->
<string name="disable_phantom_process_monitor_summary">Disable restrictions on the system resource usage of the app child processes</string>
+ <!-- Developer settings: Title for force enabling Notes role. [CHAR LIMIT=50]-->
+ <string name="enable_notes_role_title">Force enable Notes role</string>
+ <!-- Developer settings: Summary for disabling phantom process monitoring. [CHAR LIMIT=NONE]-->
+ <string name="enable_notes_role_summary">Enable note-taking system integrations via the Notes role. If the Notes role is already enabled, does nothing.</string>
+
+
<!-- BT LE Audio Device: Media Broadcast -->
<!-- The title of the Media Broadcast Dialog [CHAR LIMIT=none] -->
<string name="bluetooth_broadcast_dialog_title">Broadcast</string>
@@ -12041,4 +12057,7 @@
<!-- Warning message when we try to dock an app not supporting multiple instances split into multiple sides [CHAR LIMIT=NONE] -->
<string name="dock_multi_instances_not_supported_text">"This app can only be opened in 1 window"</string>
+
+ <!-- [CHAR LIMIT=30] Label used to open Note Task -->
+ <string name="note_task_button_label">Notetaking</string>
</resources>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 9e1dbad..e7aac3e 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -713,6 +713,11 @@
android:title="@string/disable_phantom_process_monitor_title"
android:summary="@string/disable_phantom_process_monitor_summary" />
+ <SwitchPreference
+ android:key="force_enable_notes_role"
+ android:title="@string/enable_notes_role_title"
+ android:summary="@string/enable_notes_role_summary" />
+
</PreferenceCategory>
<PreferenceCategory
diff --git a/res/xml/regional_preference_main_page.xml b/res/xml/regional_preference_main_page.xml
index de9d3d8..27a1d83 100644
--- a/res/xml/regional_preference_main_page.xml
+++ b/res/xml/regional_preference_main_page.xml
@@ -66,4 +66,11 @@
android:value="arg_value_language_select" />
</Preference>
+ <com.android.settingslib.widget.FooterPreference
+ android:key="regional_pref_footer"
+ android:title="@string/title_regional_pref_footer"
+ android:selectable="false"
+ settings:searchable="false"
+ settings:controller="com.android.settings.regionalpreferences.RegionalFooterPreferenceController"/>
+
</PreferenceScreen>
diff --git a/res/xml/security_settings_face.xml b/res/xml/security_settings_face.xml
index f0d0350..9fc8a1f 100644
--- a/res/xml/security_settings_face.xml
+++ b/res/xml/security_settings_face.xml
@@ -80,5 +80,6 @@
<com.android.settingslib.widget.FooterPreference
android:key="security_face_footer"
+ settings:searchable="false"
settings:controller="com.android.settings.biometrics.face.FaceSettingsFooterPreferenceController" />
</PreferenceScreen>
diff --git a/res/xml/wifi_hotspot_security.xml b/res/xml/wifi_hotspot_security.xml
new file mode 100644
index 0000000..3993d47
--- /dev/null
+++ b/res/xml/wifi_hotspot_security.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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_security"
+ settings:searchable="false">
+
+ <com.android.settingslib.widget.SelectorWithWidgetPreference
+ android:key="wifi_hotspot_security_wpa3"
+ android:title="@string/wifi_security_sae"/>
+
+ <com.android.settingslib.widget.SelectorWithWidgetPreference
+ android:key="wifi_hotspot_security_wpa2_wpa3"
+ android:title="@string/wifi_security_psk_sae"/>
+
+ <com.android.settingslib.widget.SelectorWithWidgetPreference
+ android:key="wifi_hotspot_security_wpa2"
+ android:title="@string/wifi_security_wpa2"/>
+
+ <com.android.settingslib.widget.SelectorWithWidgetPreference
+ android:key="wifi_hotspot_security_none"
+ android:title="@string/wifi_security_none"/>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:title="@string/wifi_hotspot_security_footer"/>
+
+</PreferenceScreen>
diff --git a/res/xml/wifi_tether_settings.xml b/res/xml/wifi_tether_settings.xml
index 3023a6e..a85d9ea 100644
--- a/res/xml/wifi_tether_settings.xml
+++ b/res/xml/wifi_tether_settings.xml
@@ -32,6 +32,13 @@
android:entries="@array/wifi_tether_security"
android:entryValues="@array/wifi_tether_security_values"/>
+ <Preference
+ android:key="wifi_hotspot_security"
+ android:title="@string/wifi_security"
+ android:summary="@string/summary_placeholder"
+ android:fragment="com.android.settings.wifi.tether.WifiHotspotSecuritySettings"
+ settings:isPreferenceVisible="@bool/config_show_wifi_hotspot_speed"/>
+
<com.android.settings.widget.ValidatedEditTextPreference
android:key="wifi_tether_network_password"
android:persistent="false"
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 388a510..68b1a48 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -134,6 +134,8 @@
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
+ public static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
+
public static final String OS_PKG = "os";
/**
diff --git a/src/com/android/settings/applications/AppStateOverlayBridge.java b/src/com/android/settings/applications/AppStateOverlayBridge.java
index 64a6ea1..2276b19 100644
--- a/src/com/android/settings/applications/AppStateOverlayBridge.java
+++ b/src/com/android/settings/applications/AppStateOverlayBridge.java
@@ -19,6 +19,7 @@
import android.app.AppOpsManager;
import android.content.Context;
+import com.android.settings.Utils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.applications.ApplicationsState.AppFilter;
@@ -59,7 +60,7 @@
private static final List<String> DISABLE_PACKAGE_LIST = new ArrayList<>();
static {
- DISABLE_PACKAGE_LIST.add("com.android.systemui");
+ DISABLE_PACKAGE_LIST.add(Utils.SYSTEMUI_PACKAGE_NAME);
}
public OverlayState(PermissionState permissionState) {
diff --git a/src/com/android/settings/applications/RecentAppStatsMixin.java b/src/com/android/settings/applications/RecentAppStatsMixin.java
index 6705b25..4e8f795 100644
--- a/src/com/android/settings/applications/RecentAppStatsMixin.java
+++ b/src/com/android/settings/applications/RecentAppStatsMixin.java
@@ -17,6 +17,7 @@
package com.android.settings.applications;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
+import static com.android.settings.Utils.SYSTEMUI_PACKAGE_NAME;
import android.app.Application;
import android.app.usage.UsageStats;
@@ -76,7 +77,7 @@
"android",
"com.android.phone",
SETTINGS_PACKAGE_NAME,
- "com.android.systemui",
+ SYSTEMUI_PACKAGE_NAME,
"com.android.providers.calendar",
"com.android.providers.media"
));
diff --git a/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java b/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
index d794de6..3c90bf3 100644
--- a/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
+++ b/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
@@ -38,6 +38,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
@@ -57,7 +58,7 @@
static final List<String> IGNORE_PACKAGE_LIST = new ArrayList<>();
static {
- IGNORE_PACKAGE_LIST.add("com.android.systemui");
+ IGNORE_PACKAGE_LIST.add(Utils.SYSTEMUI_PACKAGE_NAME);
}
/**
diff --git a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java
index 014971a..742831e 100644
--- a/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java
+++ b/src/com/android/settings/applications/specialaccess/turnscreenon/TurnScreenOnSettings.java
@@ -37,6 +37,7 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
@@ -58,7 +59,7 @@
static final List<String> IGNORE_PACKAGE_LIST = new ArrayList<>();
static {
- IGNORE_PACKAGE_LIST.add("com.android.systemui");
+ IGNORE_PACKAGE_LIST.add(Utils.SYSTEMUI_PACKAGE_NAME);
}
/**
diff --git a/src/com/android/settings/biometrics/BiometricNavigationUtils.java b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
index 32d3a32..2d0744b 100644
--- a/src/com/android/settings/biometrics/BiometricNavigationUtils.java
+++ b/src/com/android/settings/biometrics/BiometricNavigationUtils.java
@@ -26,6 +26,9 @@
import android.os.UserHandle;
import android.os.UserManager;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.Nullable;
+
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settingslib.RestrictedLockUtils;
@@ -49,15 +52,23 @@
*
* @param className The class name of Settings screen to launch.
* @param extras Extras to put into the launching {@link Intent}.
+ * @param launcher Launcher to launch activity if non-quiet mode
* @return true if the Settings screen is launching.
*/
- public boolean launchBiometricSettings(Context context, String className, Bundle extras) {
+ public boolean launchBiometricSettings(Context context, String className, Bundle extras,
+ @Nullable ActivityResultLauncher<Intent> launcher) {
final Intent quietModeDialogIntent = getQuietModeDialogIntent(context);
if (quietModeDialogIntent != null) {
context.startActivity(quietModeDialogIntent);
return false;
}
- context.startActivity(getSettingsPageIntent(className, extras));
+
+ final Intent settingsPageIntent = getSettingsPageIntent(className, extras);
+ if (launcher != null) {
+ launcher.launch(settingsPageIntent);
+ } else {
+ context.startActivity(settingsPageIntent);
+ }
return true;
}
diff --git a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
index 76a23a5..2f9ae6b 100644
--- a/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
+++ b/src/com/android/settings/biometrics/BiometricStatusPreferenceController.java
@@ -17,10 +17,14 @@
package com.android.settings.biometrics;
import android.content.Context;
+import android.content.Intent;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import com.android.internal.widget.LockPatternUtils;
@@ -29,6 +33,8 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.overlay.FeatureFactory;
+import java.lang.ref.WeakReference;
+
public abstract class BiometricStatusPreferenceController extends BasePreferenceController {
protected final UserManager mUm;
@@ -39,6 +45,8 @@
private final BiometricNavigationUtils mBiometricNavigationUtils;
private final ActiveUnlockStatusUtils mActiveUnlockStatusUtils;
+ @NonNull private WeakReference<ActivityResultLauncher<Intent>> mLauncherWeakReference =
+ new WeakReference<>(null);
/**
* @return true if the controller should be shown exclusively.
@@ -118,14 +126,31 @@
preference.setSummary(getSummaryText());
}
+ /**
+ * Set ActivityResultLauncher that will be used later during handlePreferenceTreeClick()
+ *
+ * @param preference the preference being compared
+ * @param launcher the ActivityResultLauncher
+ * @return {@code true} if matched preference.
+ */
+ public boolean setPreferenceTreeClickLauncher(@NonNull Preference preference,
+ @Nullable ActivityResultLauncher<Intent> launcher) {
+ if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
+ return false;
+ }
+
+ mLauncherWeakReference = new WeakReference<>(launcher);
+ return true;
+ }
+
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return super.handlePreferenceTreeClick(preference);
}
- return mBiometricNavigationUtils.launchBiometricSettings(
- preference.getContext(), getSettingsClassName(), preference.getExtras());
+ return mBiometricNavigationUtils.launchBiometricSettings(preference.getContext(),
+ getSettingsClassName(), preference.getExtras(), mLauncherWeakReference.get());
}
protected int getUserId() {
diff --git a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
index 7b02b9d..052b8cd 100644
--- a/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
+++ b/src/com/android/settings/biometrics/combination/BiometricsSettingsBase.java
@@ -33,6 +33,9 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -42,14 +45,19 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
+import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper;
+import java.util.Collection;
+import java.util.List;
+
/**
* Base fragment with the confirming credential functionality for combined biometrics settings.
*/
@@ -78,6 +86,18 @@
@Nullable private String mRetryPreferenceKey = null;
@Nullable private Bundle mRetryPreferenceExtra = null;
+ private final ActivityResultLauncher<Intent> mFaceOrFingerprintPreferenceLauncher =
+ registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),
+ this::onFaceOrFingerprintPreferenceResult);
+
+ private void onFaceOrFingerprintPreferenceResult(@Nullable ActivityResult result) {
+ if (result != null && result.getResultCode() == BiometricEnrollBase.RESULT_TIMEOUT) {
+ // When "Face Unlock" or "Fingerprint Unlock" is closed due to entering onStop(),
+ // "Face & Fingerprint Unlock" shall also close itself and back to "Security" page.
+ finish();
+ }
+ }
+
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -165,7 +185,7 @@
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
extras.putInt(BiometricEnrollBase.EXTRA_KEY_SENSOR_ID, sensorId);
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
- super.onPreferenceTreeClick(preference);
+ onFaceOrFingerprintPreferenceTreeClick(preference);
} catch (IllegalStateException e) {
if (retry) {
mRetryPreferenceKey = preference.getKey();
@@ -200,7 +220,7 @@
final Bundle extras = preference.getExtras();
extras.putByteArray(ChooseLockSettingsHelper.EXTRA_KEY_CHALLENGE_TOKEN, token);
extras.putLong(BiometricEnrollBase.EXTRA_KEY_CHALLENGE, challenge);
- super.onPreferenceTreeClick(preference);
+ onFaceOrFingerprintPreferenceTreeClick(preference);
} catch (IllegalStateException e) {
if (retry) {
mRetryPreferenceKey = preference.getKey();
@@ -224,6 +244,33 @@
return BiometricUtils.requestGatekeeperHat(context, gkPwHandle, userId, challenge);
}
+ /**
+ * Handle preference tree click action for "Face Unlock" or "Fingerprint Unlock" with a launcher
+ * because "Face & Fingerprint Unlock" has to close itself when it gets a specific activity
+ * error code.
+ *
+ * @param preference "Face Unlock" or "Fingerprint Unlock" preference.
+ */
+ private void onFaceOrFingerprintPreferenceTreeClick(@NonNull Preference preference) {
+ Collection<List<AbstractPreferenceController>> controllers = getPreferenceControllers();
+ for (List<AbstractPreferenceController> controllerList : controllers) {
+ for (AbstractPreferenceController controller : controllerList) {
+ if (controller instanceof BiometricStatusPreferenceController) {
+ final BiometricStatusPreferenceController biometricController =
+ (BiometricStatusPreferenceController) controller;
+ if (biometricController.setPreferenceTreeClickLauncher(preference,
+ mFaceOrFingerprintPreferenceLauncher)) {
+ if (biometricController.handlePreferenceTreeClick(preference)) {
+ writePreferenceClickMetric(preference);
+ }
+ biometricController.setPreferenceTreeClickLauncher(preference, null);
+ return;
+ }
+ }
+ }
+ }
+ }
+
@Override
public boolean onPreferenceTreeClick(Preference preference) {
return onRetryPreferenceTreeClick(preference, true)
diff --git a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
index d3e8fa7..8cc6bc4 100644
--- a/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
+++ b/src/com/android/settings/biometrics/combination/CombinedBiometricStatusUtils.java
@@ -133,6 +133,19 @@
}
/**
+ * Returns the class name of the Settings page corresponding to combined biometric settings
+ * based on the current user.
+ */
+ public String getSettingsClassNameBasedOnUser() {
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager != null && userManager.isProfile()) {
+ return getProfileSettingsClassName();
+ } else {
+ return getSettingsClassName();
+ }
+ }
+
+ /**
* Returns the class name of the Settings page corresponding to combined biometric settings.
*/
public String getSettingsClassName() {
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index 3e8c458..efd9753 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -592,6 +592,10 @@
return data;
}
+ protected boolean isFaceStrong() {
+ return mIsFaceStrong;
+ }
+
private void onFaceStrengthChanged() {
// Set up and show the "less secure" info section if necessary.
if (!mIsFaceStrong && getResources().getBoolean(
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java b/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java
index a3a745d..aabbf6f 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollParentalConsent.java
@@ -47,13 +47,14 @@
R.string.security_settings_face_enroll_introduction_control_consent_title,
R.string.security_settings_face_enroll_introduction_control_consent_message,
R.string.security_settings_face_enroll_introduction_consent_message_0,
+ R.string.security_settings_face_enroll_introduction_consent_message_0_class3,
R.string.security_settings_face_enroll_introduction_info_consent_less_secure
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setDescriptionText(R.string.security_settings_face_enroll_introduction_consent_message_0);
+ updateDescriptionText();
}
@Override
@@ -144,4 +145,16 @@
public int getMetricsCategory() {
return SettingsEnums.FACE_PARENTAL_CONSENT;
}
+
+ @Override
+ protected void updateDescriptionText() {
+ super.updateDescriptionText();
+ if (isFaceStrong()) {
+ setDescriptionText(getString(
+ R.string.security_settings_face_enroll_introduction_consent_message_0_class3));
+ } else {
+ setDescriptionText(
+ R.string.security_settings_face_enroll_introduction_consent_message_0);
+ }
+ }
}
diff --git a/src/com/android/settings/biometrics/face/FaceSettings.java b/src/com/android/settings/biometrics/face/FaceSettings.java
index cf2c09b..c300bae 100644
--- a/src/com/android/settings/biometrics/face/FaceSettings.java
+++ b/src/com/android/settings/biometrics/face/FaceSettings.java
@@ -325,6 +325,8 @@
mFaceManager.revokeChallenge(mSensorId, mUserId, mChallenge);
mToken = null;
}
+ // Let parent "Face & Fingerprint Unlock" can use this error code to close itself.
+ setResult(RESULT_TIMEOUT);
finish();
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java
index c33ae17..7f4142d 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollParentalConsent.java
@@ -131,4 +131,12 @@
public int getMetricsCategory() {
return SettingsEnums.FINGERPRINT_PARENTAL_CONSENT;
}
+
+
+ @Override
+ protected void updateDescriptionText() {
+ super.updateDescriptionText();
+ setDescriptionText(
+ R.string.security_settings_fingerprint_enroll_introduction_consent_message);
+ }
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 88a05c3..f60cd0c 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -669,6 +669,7 @@
public void onStop() {
super.onStop();
if (!getActivity().isChangingConfigurations() && !mLaunchedConfirm && !mIsEnrolling) {
+ setResult(RESULT_TIMEOUT);
getActivity().finish();
}
}
@@ -874,6 +875,12 @@
} else if (requestCode == AUTO_ADD_FIRST_FINGERPRINT_REQUEST) {
if (resultCode != RESULT_FINISHED || data == null) {
Log.d(TAG, "Add first fingerprint, fail or null data, result:" + resultCode);
+ if (resultCode == BiometricEnrollBase.RESULT_TIMEOUT) {
+ // If "Fingerprint Unlock" is closed because of timeout, notify result code
+ // back because "Face & Fingerprint Unlock" has to close itself for timeout
+ // case.
+ setResult(resultCode);
+ }
finish();
return;
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingIconTouchDialog.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingIconTouchDialog.java
index e86d755..f4fe7ff 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingIconTouchDialog.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollEnrollingIconTouchDialog.java
@@ -18,14 +18,11 @@
import android.app.Dialog;
import android.app.settings.SettingsEnums;
-import android.content.Context;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
-import androidx.lifecycle.ViewModelProvider;
import com.android.settings.R;
-import com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
@@ -33,15 +30,6 @@
*/
public class FingerprintEnrollEnrollingIconTouchDialog extends InstrumentedDialogFragment {
- private FingerprintEnrollEnrollingViewModel mViewModel;
-
- @Override
- public void onAttach(Context context) {
- mViewModel = new ViewModelProvider(getActivity()).get(
- FingerprintEnrollEnrollingViewModel.class);
- super.onAttach(context);
- }
-
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
@@ -50,10 +38,7 @@
.setMessage(R.string.security_settings_fingerprint_enroll_touch_dialog_message)
.setPositiveButton(
R.string.security_settings_fingerprint_enroll_dialog_ok,
- (dialog, which) -> {
- dialog.dismiss();
- mViewModel.onIconTouchDialogDismiss();
- });
+ (dialog, which) -> dialog.dismiss());
return builder.create();
}
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
index 62928af..d84ce61 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.java
@@ -27,7 +27,6 @@
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_IS_GENERATING_CHALLENGE;
import static com.android.settings.biometrics2.ui.viewmodel.AutoCredentialViewModel.CREDENTIAL_VALID;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
@@ -518,10 +517,6 @@
SKIP_SETUP_FIND_FPS_DIALOG_TAG);
break;
}
- case FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG: {
- onSetActivityResult(new ActivityResult(BiometricEnrollBase.RESULT_TIMEOUT, null));
- break;
- }
case FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED: {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
index 025c58b..12584ee 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModel.java
@@ -61,24 +61,18 @@
public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG = 1;
/**
- * Icon touch dialog dismiss
- */
- public static final int FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG = 2;
-
- /**
* Has got latest cancelled event due to user skip
*/
- public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP = 3;
+ public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP = 2;
/**
* Has got latest cancelled event due to back key
*/
- public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED = 4;
+ public static final int FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED = 3;
@IntDef(prefix = { "FINGERPRINT_ENROLL_ENROLLING_ACTION_" }, value = {
FINGERPRINT_ENROLL_ENROLLING_ACTION_DONE,
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG,
- FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG,
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP,
FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED
})
@@ -116,7 +110,6 @@
private final Vibrator mVibrator;
private final MutableLiveData<Integer> mActionLiveData = new MutableLiveData<>();
- private final MutableLiveData<Integer> mIconTouchDialogLiveData = new MutableLiveData<>();
private final MutableLiveData<ErrorDialogData> mErrorDialogLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> mErrorDialogActionLiveData = new MutableLiveData<>();
@@ -234,17 +227,6 @@
}
/**
- * Icon touch dialog dismiss
- */
- public void onIconTouchDialogDismiss() {
- final int action = FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG;
- if (DEBUG) {
- Log.d(TAG, "onIconTouchDialogDismiss, post action " + action);
- }
- mActionLiveData.postValue(action);
- }
-
- /**
* get enroll stage threshold
*/
public float getEnrollStageThreshold(int index) {
diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
index 588bc73..620cfb0 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
@@ -42,6 +42,8 @@
import com.android.settings.R;
import com.android.settingslib.bluetooth.BluetoothDiscoverableTimeoutReceiver;
+import kotlin.Unit;
+
import java.time.Duration;
/**
@@ -100,12 +102,15 @@
case BluetoothAdapter.STATE_ON:
case BluetoothAdapter.STATE_TURNING_ON: {
- Intent intent = new Intent(this, RequestPermissionHelperActivity.class);
- intent.putExtra(RequestPermissionHelperActivity.EXTRA_APP_LABEL, mAppLabel);
- intent.setAction(RequestPermissionHelperActivity
- .ACTION_INTERNAL_REQUEST_BT_OFF);
-
- startActivityForResult(intent, 0);
+ RequestPermissionHelper.INSTANCE.requestDisable(this, mAppLabel,
+ () -> {
+ onDisableConfirmed();
+ return Unit.INSTANCE;
+ },
+ () -> {
+ cancelAndFinish();
+ return Unit.INSTANCE;
+ });
} break;
default: {
@@ -126,18 +131,17 @@
* case via the broadcast receiver.
*/
- /*
- * Start the helper activity to:
- * 1) ask the user about enabling bt AND discovery
- * 2) enable BT upon confirmation
- */
- Intent intent = new Intent(this, RequestPermissionHelperActivity.class);
- intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
- intent.putExtra(RequestPermissionHelperActivity.EXTRA_APP_LABEL, mAppLabel);
- if (mRequest == REQUEST_ENABLE_DISCOVERABLE) {
- intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout);
- }
- startActivityForResult(intent, 0);
+ // Start the helper activity to ask the user about enabling bt AND discovery
+ RequestPermissionHelper.INSTANCE.requestEnable(this, mAppLabel,
+ mRequest == REQUEST_ENABLE_DISCOVERABLE ? mTimeout : -1,
+ () -> {
+ onEnableConfirmed();
+ return Unit.INSTANCE;
+ },
+ () -> {
+ cancelAndFinish();
+ return Unit.INSTANCE;
+ });
} break;
case BluetoothAdapter.STATE_ON: {
@@ -202,42 +206,27 @@
mDialog.show();
}
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK) {
- cancelAndFinish();
- return;
+ private void onEnableConfirmed() {
+ mBluetoothAdapter.enable();
+ if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
+ proceedAndFinish();
+ } else {
+ // If BT is not up yet, show "Turning on Bluetooth..."
+ mReceiver = new StateChangeReceiver();
+ registerReceiver(mReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
+ createDialog();
}
+ }
- switch (mRequest) {
- case REQUEST_ENABLE:
- case REQUEST_ENABLE_DISCOVERABLE: {
- if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
- proceedAndFinish();
- } else {
- // If BT is not up yet, show "Turning on Bluetooth..."
- mReceiver = new StateChangeReceiver();
- registerReceiver(mReceiver, new IntentFilter(
- BluetoothAdapter.ACTION_STATE_CHANGED));
- createDialog();
- }
- } break;
-
- case REQUEST_DISABLE: {
- if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) {
- proceedAndFinish();
- } else {
- // If BT is not up yet, show "Turning off Bluetooth..."
- mReceiver = new StateChangeReceiver();
- registerReceiver(mReceiver, new IntentFilter(
- BluetoothAdapter.ACTION_STATE_CHANGED));
- createDialog();
- }
- } break;
-
- default: {
- cancelAndFinish();
- } break;
+ private void onDisableConfirmed() {
+ mBluetoothAdapter.disable();
+ if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) {
+ proceedAndFinish();
+ } else {
+ // If BT is not up yet, show "Turning off Bluetooth..."
+ mReceiver = new StateChangeReceiver();
+ registerReceiver(mReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
+ createDialog();
}
}
diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelper.kt b/src/com/android/settings/bluetooth/RequestPermissionHelper.kt
new file mode 100644
index 0000000..000a7d1
--- /dev/null
+++ b/src/com/android/settings/bluetooth/RequestPermissionHelper.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2023 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.bluetooth
+
+import android.content.Context
+import android.os.UserManager
+import androidx.appcompat.app.AlertDialog
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
+import com.android.settingslib.spaprivileged.framework.common.userManager
+
+object RequestPermissionHelper {
+ fun requestEnable(
+ context: Context,
+ appLabel: CharSequence?,
+ timeout: Int,
+ onAllow: () -> Unit,
+ onDeny: () -> Unit,
+ ) {
+ if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
+ // Don't even show the dialog if configured this way
+ onAllow()
+ return
+ }
+ AlertDialog.Builder(context).apply {
+ setMessage(context.getEnableMessage(timeout, appLabel))
+ setPositiveButton(R.string.allow) { _, _ ->
+ if (context.isDisallowBluetooth()) onDeny() else onAllow()
+ }
+ setNegativeButton(R.string.deny) { _, _ -> onDeny() }
+ setOnCancelListener { onDeny() }
+ }.show()
+ }
+
+ fun requestDisable(
+ context: Context,
+ appLabel: CharSequence?,
+ onAllow: () -> Unit,
+ onDeny: () -> Unit,
+ ) {
+ if (context.resources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
+ // Don't even show the dialog if configured this way
+ onAllow()
+ return
+ }
+ AlertDialog.Builder(context).apply {
+ setMessage(context.getDisableMessage(appLabel))
+ setPositiveButton(R.string.allow) { _, _ -> onAllow() }
+ setNegativeButton(R.string.deny) { _, _ -> onDeny() }
+ setOnCancelListener { onDeny() }
+ }.show()
+ }
+}
+
+// If Bluetooth is disallowed, don't try to enable it, show policy transparency message instead.
+private fun Context.isDisallowBluetooth() =
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH)) {
+ devicePolicyManager.createAdminSupportIntent(UserManager.DISALLOW_BLUETOOTH)
+ ?.let { startActivity(it) }
+ true
+ } else false
+
+private fun Context.getEnableMessage(timeout: Int, name: CharSequence?): String = when {
+ timeout < 0 -> when (name) {
+ null -> getString(R.string.bluetooth_ask_enablement_no_name)
+ else -> getString(R.string.bluetooth_ask_enablement, name)
+ }
+
+ timeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER -> when (name) {
+ null -> getString(R.string.bluetooth_ask_enablement_and_lasting_discovery_no_name)
+ else -> getString(R.string.bluetooth_ask_enablement_and_lasting_discovery, name)
+ }
+
+ else -> when (name) {
+ null -> getString(R.string.bluetooth_ask_enablement_and_discovery_no_name, timeout)
+ else -> getString(R.string.bluetooth_ask_enablement_and_discovery, name, timeout)
+ }
+}
+
+private fun Context.getDisableMessage(name: CharSequence?): String =
+ when (name) {
+ null -> getString(R.string.bluetooth_ask_disablement_no_name)
+ else -> getString(R.string.bluetooth_ask_disablement, name)
+ }
diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
deleted file mode 100644
index 99ac928..0000000
--- a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2009 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.bluetooth;
-
-import android.app.Activity;
-import android.app.admin.DevicePolicyManager;
-import android.bluetooth.BluetoothAdapter;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.util.Log;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
-import com.android.settings.R;
-
-/**
- * RequestPermissionHelperActivity asks the user whether to toggle Bluetooth.
- *
- * TODO: This activity isn't needed - this should be folded in RequestPermissionActivity
- */
-public class RequestPermissionHelperActivity extends AlertActivity implements
- DialogInterface.OnClickListener {
- private static final String TAG = "RequestPermissionHelperActivity";
-
- public static final String ACTION_INTERNAL_REQUEST_BT_ON =
- "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON";
-
- public static final String ACTION_INTERNAL_REQUEST_BT_OFF =
- "com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_OFF";
-
- public static final String EXTRA_APP_LABEL =
- "com.android.settings.bluetooth.extra.APP_LABEL";
-
- private BluetoothAdapter mBluetoothAdapter;
-
- private CharSequence mAppLabel;
-
- private int mTimeout = -1;
-
- private int mRequest;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setResult(RESULT_CANCELED);
-
- // Note: initializes mBluetoothAdapter and returns true on error
- if (!parseIntent()) {
- finish();
- return;
- }
-
- if (getResources().getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog)) {
- // Don't even show the dialog if configured this way
- onClick(null, BUTTON_POSITIVE);
- dismiss();
- }
-
- createDialog();
- }
-
- void createDialog() {
- final AlertController.AlertParams p = mAlertParams;
-
- switch (mRequest) {
- case RequestPermissionActivity.REQUEST_ENABLE: {
- if (mTimeout < 0) {
- p.mMessage = mAppLabel != null
- ? getString(R.string.bluetooth_ask_enablement, mAppLabel)
- : getString(R.string.bluetooth_ask_enablement_no_name);
- } else if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) {
- p.mMessage = mAppLabel != null
- ? getString(
- R.string.bluetooth_ask_enablement_and_lasting_discovery,
- mAppLabel)
- : getString(
- R.string.bluetooth_ask_enablement_and_lasting_discovery_no_name);
- } else {
- p.mMessage = mAppLabel != null
- ? getString(R.string.bluetooth_ask_enablement_and_discovery,
- mAppLabel, mTimeout)
- : getString(R.string.bluetooth_ask_enablement_and_discovery_no_name,
- mTimeout);
- }
- } break;
-
- case RequestPermissionActivity.REQUEST_DISABLE: {
- p.mMessage = mAppLabel != null
- ? getString(R.string.bluetooth_ask_disablement, mAppLabel)
- : getString(R.string.bluetooth_ask_disablement_no_name);
- } break;
- }
-
- p.mPositiveButtonText = getString(R.string.allow);
- p.mPositiveButtonListener = this;
- p.mNegativeButtonText = getString(R.string.deny);
-
- setupAlert();
- }
-
- public void onClick(DialogInterface dialog, int which) {
- switch (mRequest) {
- case RequestPermissionActivity.REQUEST_ENABLE:
- case RequestPermissionActivity.REQUEST_ENABLE_DISCOVERABLE: {
- UserManager userManager = getSystemService(UserManager.class);
- if (userManager.hasUserRestriction(UserManager.DISALLOW_BLUETOOTH)) {
- // If Bluetooth is disallowed, don't try to enable it, show policy transparency
- // message instead.
- DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
- Intent intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_BLUETOOTH);
- if (intent != null) {
- startActivity(intent);
- }
- } else {
- mBluetoothAdapter.enable();
- setResult(Activity.RESULT_OK);
- }
- } break;
-
- case RequestPermissionActivity.REQUEST_DISABLE: {
- mBluetoothAdapter.disable();
- setResult(Activity.RESULT_OK);
- } break;
- }
- }
-
- /**
- * Parse the received Intent and initialize mBluetoothAdapter.
- * @return true if an error occurred; false otherwise
- */
- private boolean parseIntent() {
- Intent intent = getIntent();
- if (intent == null) {
- return false;
- }
-
- String action = intent.getAction();
- if (ACTION_INTERNAL_REQUEST_BT_ON.equals(action)) {
- mRequest = RequestPermissionActivity.REQUEST_ENABLE;
- if (intent.hasExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION)) {
- // Value used for display purposes. Not range checking.
- mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
- BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
- }
- } else if (ACTION_INTERNAL_REQUEST_BT_OFF.equals(action)) {
- mRequest = RequestPermissionActivity.REQUEST_DISABLE;
- } else {
- return false;
- }
-
- mAppLabel = getIntent().getCharSequenceExtra(EXTRA_APP_LABEL);
-
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- if (mBluetoothAdapter == null) {
- Log.e(TAG, "Error: there's a problem starting Bluetooth");
- return false;
- }
-
- return true;
- }
-}
diff --git a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
index b30aee4..66f2f1b 100644
--- a/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
@@ -25,6 +25,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
import com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController;
import com.android.settings.bluetooth.BluetoothSwitchPreferenceController;
import com.android.settings.dashboard.DashboardFragment;
@@ -45,8 +46,6 @@
private static final String TAG = "BluetoothDashboardFrag";
private static final String KEY_BLUETOOTH_SCREEN_FOOTER = "bluetooth_screen_footer";
- private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
- private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -112,8 +111,8 @@
@VisibleForTesting
boolean isAlwaysDiscoverable(String callingAppPackageName, String action) {
return TextUtils.equals(SLICE_ACTION, action) ? false
- : TextUtils.equals(SETTINGS_PACKAGE_NAME, callingAppPackageName)
- || TextUtils.equals(SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
+ : TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, callingAppPackageName)
+ || TextUtils.equals(Utils.SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
}
/**
diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
index 3c6077e..9286b81 100644
--- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java
@@ -28,6 +28,7 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
+import com.android.settings.Utils;
import com.android.settings.core.SettingsUIDeviceConfig;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -38,7 +39,6 @@
public class ConnectedDeviceDashboardFragment extends DashboardFragment {
private static final String TAG = "ConnectedDeviceFrag";
- private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String SLICE_ACTION = "com.android.settings.SEARCH_RESULT_TRAMPOLINE";
@@ -92,8 +92,8 @@
@VisibleForTesting
boolean isAlwaysDiscoverable(String callingAppPackageName, String action) {
return TextUtils.equals(SLICE_ACTION, action) ? false
- : TextUtils.equals(SETTINGS_PACKAGE_NAME, callingAppPackageName)
- || TextUtils.equals(SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
+ : TextUtils.equals(Utils.SETTINGS_PACKAGE_NAME, callingAppPackageName)
+ || TextUtils.equals(Utils.SYSTEMUI_PACKAGE_NAME, callingAppPackageName);
}
/**
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 68fe08e..65d3be0 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -16,6 +16,7 @@
package com.android.settings.development;
+import static android.provider.Settings.Global.DEVELOPMENT_SETTINGS_ENABLED;
import static android.service.quicksettings.TileService.ACTION_QS_TILE_PREFERENCES;
import android.app.Activity;
@@ -27,12 +28,18 @@
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserManager;
+import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -173,11 +180,48 @@
}
};
+ private final Uri mDevelopEnabled = Settings.Global.getUriFor(DEVELOPMENT_SETTINGS_ENABLED);
+ private final ContentObserver mDeveloperSettingsObserver = new ContentObserver(new Handler(
+ Looper.getMainLooper())) {
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ super.onChange(selfChange, uri);
+ final boolean developmentEnabledState =
+ DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(getContext());
+ final boolean switchState = mSwitchBar.isChecked();
+
+ // when developer options is enabled, but it is disabled by other privilege apps like:
+ // adb command, we should disable all items and finish the activity.
+ if (developmentEnabledState != switchState) {
+ if (developmentEnabledState) {
+ return;
+ }
+ disableDeveloperOptions();
+ getActivity().runOnUiThread(() -> finishFragment());
+ }
+ }
+ };
+
public DevelopmentSettingsDashboardFragment() {
super(UserManager.DISALLOW_DEBUGGING_FEATURES);
}
@Override
+ public void onStart() {
+ super.onStart();
+ final ContentResolver cr = getContext().getContentResolver();
+ cr.registerContentObserver(mDevelopEnabled, false, mDeveloperSettingsObserver);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ final ContentResolver cr = getContext().getContentResolver();
+ cr.unregisterContentObserver(mDeveloperSettingsObserver);
+ }
+
+ @Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
SearchMenuController.init(this);
@@ -689,6 +733,7 @@
controllers.add(new PhantomProcessPreferenceController(context));
controllers.add(new ContrastPreferenceController(
context, context.getSystemService(UiModeManager.class)));
+ controllers.add(new ForceEnableNotesRolePreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/development/ForceEnableNotesRolePreferenceController.java b/src/com/android/settings/development/ForceEnableNotesRolePreferenceController.java
new file mode 100644
index 0000000..7821bec
--- /dev/null
+++ b/src/com/android/settings/development/ForceEnableNotesRolePreferenceController.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static android.os.UserHandle.USER_CURRENT;
+
+import android.content.Context;
+import android.content.om.IOverlayManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import androidx.preference.Preference;
+import androidx.preference.SwitchPreference;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class ForceEnableNotesRolePreferenceController
+ extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
+
+ private static final String NOTES_ROLE_ENABLED_KEY =
+ "force_enable_notes_role";
+
+ private static final String OVERLAY_PACKAGE_NAME =
+ "com.android.role.notes.enabled";
+
+ private final IOverlayManager mOverlayManager;
+
+ public ForceEnableNotesRolePreferenceController(Context context) {
+ super(context);
+ mOverlayManager = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return NOTES_ROLE_ENABLED_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ setEnabled((boolean) newValue);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ ((SwitchPreference) mPreference).setChecked(isEnabled());
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ ((SwitchPreference) mPreference).setChecked(false);
+ setEnabled(false);
+ }
+
+ @VisibleForTesting
+ protected boolean isEnabled() {
+ return mContext.getResources().getBoolean(R.bool.config_enableDefaultNotes);
+ }
+
+ @VisibleForTesting
+ protected void setEnabled(boolean enabled) {
+ try {
+ mOverlayManager.setEnabled(OVERLAY_PACKAGE_NAME, enabled, USER_CURRENT);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index bf81727..e0e25df 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -121,7 +121,7 @@
final PowerManager powerManager = context.getSystemService(PowerManager.class);
final PackageManager packageManager = context.getPackageManager();
final String packageName = context.getString(
- com.android.internal.R.string.config_defaultWellbeingPackage);
+ com.android.internal.R.string.config_systemWellbeing);
try {
uid = packageManager.getApplicationInfo(packageName, /* flags= */ 0).uid;
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/settings/display/darkmode/BedtimeSettings.java b/src/com/android/settings/display/darkmode/BedtimeSettings.java
index 28121b2..5b9b4d2 100644
--- a/src/com/android/settings/display/darkmode/BedtimeSettings.java
+++ b/src/com/android/settings/display/darkmode/BedtimeSettings.java
@@ -37,7 +37,7 @@
mContext = context;
mPackageManager = context.getPackageManager();
mWellbeingPackage = mContext.getResources().getString(
- com.android.internal.R.string.config_defaultWellbeingPackage);
+ com.android.internal.R.string.config_systemWellbeing);
}
/**
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index ac72ced..496cfab 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -16,6 +16,8 @@
package com.android.settings.fuelgauge;
+import static com.android.settings.Utils.SYSTEMUI_PACKAGE_NAME;
+
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -35,9 +37,8 @@
private static final String PACKAGE_CALENDAR_PROVIDER = "com.android.providers.calendar";
private static final String PACKAGE_MEDIA_PROVIDER = "com.android.providers.media";
- private static final String PACKAGE_SYSTEMUI = "com.android.systemui";
private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER,
- PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI};
+ PACKAGE_CALENDAR_PROVIDER, SYSTEMUI_PACKAGE_NAME};
protected PackageManager mPackageManager;
protected Context mContext;
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index e622f4c..8e9d485 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -72,7 +72,7 @@
.detect());
tips.add(new BatteryDefenderDetector(batteryInfo, context).detect());
tips.add(new DockDefenderDetector(batteryInfo, context).detect());
- tips.add(new IncompatibleChargerDetector(context, batteryInfo).detect());
+ tips.add(new IncompatibleChargerDetector(context).detect());
Collections.sort(tips);
return tips;
}
diff --git a/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java b/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java
index 483e37d..cfd9a2c 100644
--- a/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java
+++ b/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetector.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import com.android.settings.fuelgauge.batterytip.tips.IncompatibleChargerTip;
import com.android.settingslib.Utils;
@@ -28,25 +27,17 @@
private static final String TAG = "IncompatibleChargerDetector";
private final Context mContext;
- private final BatteryInfo mBatteryInfo;
- public IncompatibleChargerDetector(Context context, BatteryInfo batteryInfo) {
+ public IncompatibleChargerDetector(Context context) {
mContext = context;
- mBatteryInfo = batteryInfo;
}
@Override
public BatteryTip detect() {
- int state = BatteryTip.StateType.INVISIBLE;
- boolean isIncompatibleCharging = false;
-
- // Check incompatible charging state if the device is plugged.
- if (mBatteryInfo.pluggedStatus != 0) {
- isIncompatibleCharging = Utils.containsIncompatibleChargers(mContext, TAG);
- if (isIncompatibleCharging) {
- state = BatteryTip.StateType.NEW;
- }
- }
+ final boolean isIncompatibleCharging =
+ Utils.containsIncompatibleChargers(mContext, TAG);
+ final int state = isIncompatibleCharging
+ ? BatteryTip.StateType.NEW : BatteryTip.StateType.INVISIBLE;
Log.d(TAG, "detect() state= " + state + " isIncompatibleCharging: "
+ isIncompatibleCharging);
return new IncompatibleChargerTip(state);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index d192e1a..67d3969 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -251,8 +251,7 @@
hourlyBatteryLevelsPerDay.getLevels(),
hourlyBatteryLevelsPerDay.getTimestamps(),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
- mHourlyChartLabelTextGenerator.setLatestTimestamp(getLast(getLast(
- batteryLevelData.getHourlyBatteryLevelsPerDay()).getTimestamps()))));
+ mHourlyChartLabelTextGenerator.updateSpecialCaseContext(batteryLevelData)));
}
refreshUi();
}
@@ -296,6 +295,10 @@
});
mHourlyChartView = hourlyChartView;
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
+ if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+ // This will happen when a daily slot and an hour slot are clicked together.
+ return;
+ }
if (mHourlyChartIndex == trapezoidIndex) {
return;
}
@@ -636,7 +639,11 @@
private final class HourlyChartLabelTextGenerator implements
BatteryChartViewModel.LabelTextGenerator {
- private Long mLatestTimestamp;
+ private static final int FULL_CHARGE_BATTERY_LEVEL = 100;
+
+ private boolean mIsFromFullCharge;
+ private long mFistTimestamp;
+ private long mLatestTimestamp;
@Override
public String generateText(List<Long> timestamps, int index) {
@@ -644,24 +651,37 @@
// Replaces the latest timestamp text to "now".
return mContext.getString(R.string.battery_usage_chart_label_now);
}
- return ConvertUtils.utcToLocalTimeHour(mContext, timestamps.get(index),
- mIs24HourFormat);
+ long timestamp = timestamps.get(index);
+ boolean showMinute = false;
+ if (Objects.equal(timestamp, mFistTimestamp)) {
+ if (mIsFromFullCharge) {
+ showMinute = true;
+ } else {
+ // starts from 7 days ago
+ timestamp = TimestampUtils.getLastEvenHourTimestamp(timestamp);
+ }
+ }
+ return ConvertUtils.utcToLocalTimeHour(
+ mContext, timestamp, mIs24HourFormat, showMinute);
}
@Override
public String generateFullText(List<Long> timestamps, int index) {
- if (Objects.equal(timestamps.get(index), mLatestTimestamp)) {
- // Replaces the latest timestamp text to "now".
- return mContext.getString(R.string.battery_usage_chart_label_now);
- }
return index == timestamps.size() - 1
? generateText(timestamps, index)
: mContext.getString(R.string.battery_usage_timestamps_hyphen,
generateText(timestamps, index), generateText(timestamps, index + 1));
}
- public HourlyChartLabelTextGenerator setLatestTimestamp(Long latestTimestamp) {
- this.mLatestTimestamp = latestTimestamp;
+ HourlyChartLabelTextGenerator updateSpecialCaseContext(
+ @NonNull final BatteryLevelData batteryLevelData) {
+ BatteryLevelData.PeriodBatteryLevelData firstDayLevelData =
+ batteryLevelData.getHourlyBatteryLevelsPerDay().get(0);
+ this.mIsFromFullCharge =
+ firstDayLevelData.getLevels().get(0) == FULL_CHARGE_BATTERY_LEVEL;
+ this.mFistTimestamp = firstDayLevelData.getTimestamps().get(0);
+ this.mLatestTimestamp = getLast(getLast(
+ batteryLevelData.getHourlyBatteryLevelsPerDay()).getTimestamps());
return this;
}
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index b17ccaa..445a5d1 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -16,6 +16,7 @@
package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.Utils.formatPercentage;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS;
import static java.lang.Math.round;
import static java.util.Objects.requireNonNull;
@@ -29,6 +30,7 @@
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Bundle;
+import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.view.HapticFeedbackConstants;
@@ -52,13 +54,13 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
/** A widget component to draw chart graph. */
public class BatteryChartView extends AppCompatImageView implements View.OnClickListener {
private static final String TAG = "BatteryChartView";
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
- private static final long UPDATE_STATE_DELAYED_TIME = 500L;
/** A callback listener for selected group index is updated. */
public interface OnSelectListener {
@@ -70,6 +72,7 @@
private final Rect mIndent = new Rect();
private final Rect[] mPercentageBounds = new Rect[]{new Rect(), new Rect(), new Rect()};
private final List<Rect> mAxisLabelsBounds = new ArrayList<>();
+ private final Set<Integer> mLabelDrawnIndexes = new ArraySet<>();
private BatteryChartViewModel mViewModel;
private int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
@@ -80,6 +83,7 @@
private int mTrapezoidColor;
private int mTrapezoidSolidColor;
private int mTrapezoidHoverColor;
+ private int mDefaultTextColor;
private int mTextPadding;
private Paint mDividerPaint;
private Paint mTrapezoidPaint;
@@ -134,6 +138,7 @@
// Pre-draws the view first to load style atttributions into paint.
textView.draw(new Canvas());
mTextPaint = textView.getPaint();
+ mDefaultTextColor = mTextPaint.getColor();
} else {
mTextPaint = null;
}
@@ -332,6 +337,7 @@
final int height = getHeight() - mIndent.top - mIndent.bottom;
// Draws the top divider line for 100% curve.
float offsetY = mIndent.top + mDividerWidth * .5f;
+ mDividerPaint.setColor(DIVIDER_COLOR);
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
drawPercentage(canvas, /*index=*/ 0, offsetY);
@@ -351,6 +357,7 @@
private void drawPercentage(Canvas canvas, int index, float offsetY) {
if (mTextPaint != null) {
mTextPaint.setTextAlign(Paint.Align.RIGHT);
+ mTextPaint.setColor(mDefaultTextColor);
canvas.drawText(
mPercentages[index],
getWidth(),
@@ -367,18 +374,6 @@
final float bottomY = getHeight() - mIndent.bottom;
final float startY = bottomY - mDividerHeight;
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
- // Draws each vertical dividers.
- float startX = mDividerWidth * .5f;
- for (int index = 0; index < dividerCount; index++) {
- canvas.drawLine(startX, startY, startX, bottomY, mDividerPaint);
- final float nextX = startX + mDividerWidth + unitWidth;
- // Updates the trapezoid slots for drawing.
- if (index < mTrapezoidSlots.length) {
- mTrapezoidSlots[index].mLeft = round(startX + trapezoidSlotOffset);
- mTrapezoidSlots[index].mRight = round(nextX - trapezoidSlotOffset);
- }
- startX = nextX;
- }
// Draws the axis label slot information.
if (mViewModel != null) {
final float baselineY = getHeight() - mTextPadding;
@@ -404,6 +399,26 @@
}
drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY);
}
+ // Draws each vertical dividers.
+ float startX = mDividerWidth * .5f;
+ for (int index = 0; index < dividerCount; index++) {
+ float dividerY = bottomY;
+ if (mViewModel.axisLabelPosition() == BETWEEN_TRAPEZOIDS
+ && mLabelDrawnIndexes.contains(index)) {
+ mDividerPaint.setColor(mTrapezoidSolidColor);
+ dividerY += mDividerHeight / 4f;
+ } else {
+ mDividerPaint.setColor(DIVIDER_COLOR);
+ }
+ canvas.drawLine(startX, startY, startX, dividerY, mDividerPaint);
+ final float nextX = startX + mDividerWidth + unitWidth;
+ // Updates the trapezoid slots for drawing.
+ if (index < mTrapezoidSlots.length) {
+ mTrapezoidSlots[index].mLeft = round(startX + trapezoidSlotOffset);
+ mTrapezoidSlots[index].mRight = round(nextX - trapezoidSlotOffset);
+ }
+ startX = nextX;
+ }
}
/** Gets all the axis label texts displaying area positions if they are shown. */
@@ -432,9 +447,12 @@
private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) {
final int lastIndex = displayAreas.length - 1;
+ mLabelDrawnIndexes.clear();
// Suppose first and last labels are always able to draw.
drawAxisLabelText(canvas, 0, displayAreas[0], baselineY);
+ mLabelDrawnIndexes.add(0);
drawAxisLabelText(canvas, lastIndex, displayAreas[lastIndex], baselineY);
+ mLabelDrawnIndexes.add(lastIndex);
drawAxisLabelsBetweenStartIndexAndEndIndex(canvas, displayAreas, 0, lastIndex, baselineY);
}
@@ -458,6 +476,7 @@
return;
}
drawAxisLabelText(canvas, middleIndex, displayAreas[middleIndex], baselineY);
+ mLabelDrawnIndexes.add(middleIndex);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, startIndex, middleIndex, baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
@@ -471,7 +490,9 @@
return;
}
drawAxisLabelText(canvas, middleIndex1, displayAreas[middleIndex1], baselineY);
+ mLabelDrawnIndexes.add(middleIndex1);
drawAxisLabelText(canvas, middleIndex2, displayAreas[middleIndex2], baselineY);
+ mLabelDrawnIndexes.add(middleIndex2);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, startIndex, middleIndex1, baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
@@ -488,12 +509,14 @@
private void drawAxisLabelText(
Canvas canvas, final int index, final Rect displayArea, final float baselineY) {
+ mTextPaint.setColor(mTrapezoidSolidColor);
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(
mViewModel.getText(index),
displayArea.centerX(),
baselineY,
mTextPaint);
+ mLabelDrawnIndexes.add(index);
}
private void drawTrapezoids(Canvas canvas) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index 2121c60..cd1bdef 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -313,11 +313,8 @@
@VisibleForTesting
void removeAndCacheAllUnusedPreferences() {
List<BatteryDiffEntry> entries = getBatteryDiffEntries();
- Set<String> entryKeySet = new ArraySet<>();
- for (BatteryDiffEntry entry : entries) {
- entryKeySet.add(entry.getKey());
- }
-
+ Set<String> entryKeySet = new ArraySet<>(entries.size());
+ entries.forEach(entry -> entryKeySet.add(entry.getKey()));
final int prefsCount = mAppListPreferenceGroup.getPreferenceCount();
for (int index = prefsCount - 1; index >= 0; index--) {
final Preference pref = mAppListPreferenceGroup.getPreference(index);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index c6c548f..2c98c4b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -280,12 +280,12 @@
}
/** Converts UTC timestamp to local time hour data. */
- public static String utcToLocalTimeHour(
- final Context context, final long timestamp, final boolean is24HourFormat) {
+ public static String utcToLocalTimeHour(final Context context, final long timestamp,
+ final boolean is24HourFormat, final boolean showMinute) {
final Locale locale = getLocale(context);
// e.g. for 12-hour format: 9 PM
// e.g. for 24-hour format: 09:00
- final String skeleton = is24HourFormat ? "HHm" : "ha";
+ final String skeleton = is24HourFormat ? "HHm" : (showMinute ? "hma" : "ha");
final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
return DateFormat.format(pattern, timestamp).toString();
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index ec1a4be..9aee2f7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -285,7 +285,6 @@
final Map<Integer, Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>>> resultMap =
new ArrayMap<>();
- final long dailySize = hourlyBatteryLevelsPerDay.size();
for (int dailyIndex = 0; dailyIndex < hourlyBatteryLevelsPerDay.size(); dailyIndex++) {
final Map<Integer, Map<Long, Map<String, List<AppUsagePeriod>>>> dailyMap =
new ArrayMap<>();
@@ -294,21 +293,9 @@
continue;
}
final List<Long> timestamps = hourlyBatteryLevelsPerDay.get(dailyIndex).getTimestamps();
- final long hourlySize = timestamps.size() - 1;
for (int hourlyIndex = 0; hourlyIndex < timestamps.size() - 1; hourlyIndex++) {
- // The start slot timestape is the near hour timestamp instead of the last full
- // charge time. So use rawStartTimestamp instead of reading the timestamp from
- // hourlyBatteryLevelsPerDay here.
- final long startTimestamp =
- dailyIndex == 0 && hourlyIndex == 0 && !sDebug
- ? rawStartTimestamp : timestamps.get(hourlyIndex);
- // The final slot is to show the data from last even hour until now but the
- // timestamp in hourlyBatteryLevelsPerDay is not the real value. So use current
- // timestamp instead of reading the timestamp from hourlyBatteryLevelsPerDay here.
- final long endTimestamp =
- dailyIndex == dailySize - 1 && hourlyIndex == hourlySize - 1 && !sDebug
- ? System.currentTimeMillis() : timestamps.get(hourlyIndex + 1);
-
+ final long startTimestamp = timestamps.get(hourlyIndex);
+ final long endTimestamp = timestamps.get(hourlyIndex + 1);
// Gets the app usage event list for this hourly slot first.
final List<AppUsageEvent> hourlyAppUsageEventList =
getAppUsageEventListWithinTimeRangeWithBuffer(
@@ -463,11 +450,8 @@
Collections.sort(rawTimestampList);
final long currentTime = getCurrentTimeMillis();
final List<Long> expectedTimestampList = getTimestampSlots(rawTimestampList, currentTime);
- final boolean isFromFullCharge =
- isFromFullCharge(batteryHistoryMap.get(rawTimestampList.get(0)));
interpolateHistory(
- context, rawTimestampList, expectedTimestampList, currentTime, isFromFullCharge,
- batteryHistoryMap, resultMap);
+ context, rawTimestampList, expectedTimestampList, batteryHistoryMap, resultMap);
Log.d(TAG, String.format("getHistoryMapWithExpectedTimestamps() size=%d in %d/ms",
resultMap.size(), (System.currentTimeMillis() - startTime)));
return resultMap;
@@ -496,8 +480,9 @@
}
/**
- * Computes expected timestamp slots for last full charge, which will return hourly timestamps
- * between start and end two even hour values.
+ * Computes expected timestamp slots. The start timestamp is the last full charge time.
+ * The end timestamp is current time. The middle timestamps are the sharp hour timestamps
+ * between the start and end timestamps.
*/
@VisibleForTesting
static List<Long> getTimestampSlots(final List<Long> rawTimestampList, final long currentTime) {
@@ -505,19 +490,18 @@
if (rawTimestampList.isEmpty()) {
return timestampSlots;
}
- final long rawStartTimestamp = rawTimestampList.get(0);
- // No matter the start is from last full charge or 6 days ago, use the nearest even hour.
- final long startTimestamp = getNearestEvenHourTimestamp(rawStartTimestamp);
- // Use the first even hour after the current time as the end.
- final long endTimestamp = getFirstEvenHourAfterTimestamp(currentTime);
+ final long startTimestamp = rawTimestampList.get(0);
+ final long endTimestamp = currentTime;
// If the start timestamp is later or equal the end one, return the empty list.
if (startTimestamp >= endTimestamp) {
return timestampSlots;
}
- for (long timestamp = startTimestamp; timestamp <= endTimestamp;
- timestamp += DateUtils.HOUR_IN_MILLIS) {
+ timestampSlots.add(startTimestamp);
+ for (long timestamp = TimestampUtils.getNextHourTimestamp(startTimestamp);
+ timestamp < endTimestamp; timestamp += DateUtils.HOUR_IN_MILLIS) {
timestampSlots.add(timestamp);
}
+ timestampSlots.add(endTimestamp);
return timestampSlots;
}
@@ -539,35 +523,39 @@
}
final long startTime = timestampList.get(0);
final long endTime = timestampList.get(timestampList.size() - 1);
- // If the timestamp diff is smaller than MIN_TIME_SLOT, returns the empty list directly.
- if (endTime - startTime < MIN_TIME_SLOT) {
- return dailyTimestampList;
+ for (long timestamp = startTime; timestamp < endTime;
+ timestamp = TimestampUtils.getNextDayTimestamp(timestamp)) {
+ dailyTimestampList.add(timestamp);
}
- long nextDay = getTimestampOfNextDay(startTime);
- // Only if the timestamp diff in the first day is bigger than MIN_TIME_SLOT, start from the
- // first day. Otherwise, start from the second day.
- if (nextDay - startTime >= MIN_TIME_SLOT) {
- dailyTimestampList.add(startTime);
- }
- while (nextDay < endTime) {
- dailyTimestampList.add(nextDay);
- nextDay = getTimestampOfNextDay(nextDay);
- }
- final long lastDailyTimestamp = dailyTimestampList.get(dailyTimestampList.size() - 1);
- // Only if the timestamp diff in the last day is bigger than MIN_TIME_SLOT, add the
- // last day.
- if (endTime - lastDailyTimestamp >= MIN_TIME_SLOT) {
- dailyTimestampList.add(endTime);
- }
- // The dailyTimestampList must have the start and end timestamp, otherwise, return an empty
- // list.
- if (dailyTimestampList.size() < MIN_TIMESTAMP_DATA_SIZE) {
- return new ArrayList<>();
- }
+ dailyTimestampList.add(endTime);
return dailyTimestampList;
}
@VisibleForTesting
+ static List<List<Long>> getHourlyTimestamps(final List<Long> dailyTimestamps) {
+ final List<List<Long>> hourlyTimestamps = new ArrayList<>();
+ if (dailyTimestamps.size() < MIN_DAILY_DATA_SIZE) {
+ return hourlyTimestamps;
+ }
+
+ for (int dailyIndex = 0; dailyIndex < dailyTimestamps.size() - 1; dailyIndex++) {
+ final List<Long> hourlyTimestampsPerDay = new ArrayList<>();
+ final long startTime = dailyTimestamps.get(dailyIndex);
+ final long endTime = dailyTimestamps.get(dailyIndex + 1);
+
+ hourlyTimestampsPerDay.add(startTime);
+ for (long timestamp = TimestampUtils.getNextEvenHourTimestamp(startTime);
+ timestamp < endTime; timestamp += MIN_TIME_SLOT) {
+ hourlyTimestampsPerDay.add(timestamp);
+ }
+ hourlyTimestampsPerDay.add(endTime);
+
+ hourlyTimestamps.add(hourlyTimestampsPerDay);
+ }
+ return hourlyTimestamps;
+ }
+
+ @VisibleForTesting
static boolean isFromFullCharge(@Nullable final Map<String, BatteryHistEntry> entryList) {
if (entryList == null) {
Log.d(TAG, "entryList is null in isFromFullCharge()");
@@ -603,30 +591,6 @@
}
/**
- * @return Returns the timestamp for 00:00 1 day after the given timestamp based on local
- * timezone.
- */
- @VisibleForTesting
- static long getTimestampOfNextDay(long timestamp) {
- return getTimestampWithDayDiff(timestamp, /*dayDiff=*/ 1);
- }
-
- /**
- * Returns whether currentSlot will be used in daily chart.
- */
- @VisibleForTesting
- static boolean isForDailyChart(final boolean isStartOrEnd, final long currentSlot) {
- // The start and end timestamps will always be used in daily chart.
- if (isStartOrEnd) {
- return true;
- }
-
- // The timestamps for 00:00 will be used in daily chart.
- final long startOfTheDay = getTimestampWithDayDiff(currentSlot, /*dayDiff=*/ 0);
- return currentSlot == startOfTheDay;
- }
-
- /**
* @return Returns the indexed battery usage data for each corresponding time slot.
*
* <p>There could be 2 cases of the returned value:</p>
@@ -725,9 +689,9 @@
@VisibleForTesting
@Nullable
static Map<Long, Map<String, List<AppUsagePeriod>>> buildAppUsagePeriodList(
- final List<AppUsageEvent> allAppUsageEvents, final List<BatteryEvent> batteryEventList,
+ final List<AppUsageEvent> appUsageEvents, final List<BatteryEvent> batteryEventList,
final long startTime, final long endTime) {
- if (allAppUsageEvents.isEmpty()) {
+ if (appUsageEvents.isEmpty()) {
return null;
}
@@ -735,7 +699,7 @@
// use.
final List<AppUsageEvent> deviceEvents = new ArrayList<>();
final ArrayMap<Integer, List<AppUsageEvent>> usageEventsByInstanceId = new ArrayMap<>();
- for (final AppUsageEvent event : allAppUsageEvents) {
+ for (final AppUsageEvent event : appUsageEvents) {
final AppUsageEventType eventType = event.getType();
if (eventType == AppUsageEventType.ACTIVITY_RESUMED
|| eventType == AppUsageEventType.ACTIVITY_STOPPED) {
@@ -852,9 +816,11 @@
final List<AppUsagePeriod> usagePeriodList,
final List<BatteryEvent> batteryEventList) {
final List<AppUsagePeriod> resultList = new ArrayList<>();
+ int index = 0;
for (AppUsagePeriod inputPeriod : usagePeriodList) {
long lastStartTime = inputPeriod.getStartTime();
- for (BatteryEvent batteryEvent : batteryEventList) {
+ while (index < batteryEventList.size()) {
+ BatteryEvent batteryEvent = batteryEventList.get(index);
if (batteryEvent.getTimestamp() < inputPeriod.getStartTime()) {
// Because the batteryEventList has been sorted, here is to mark the power
// connection state when the usage period starts. If power is connected when
@@ -865,6 +831,7 @@
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
lastStartTime = inputPeriod.getStartTime();
}
+ index++;
continue;
}
if (batteryEvent.getTimestamp() > inputPeriod.getEndTime()) {
@@ -883,6 +850,7 @@
} else if (batteryEvent.getType() == BatteryEventType.POWER_DISCONNECTED) {
lastStartTime = batteryEvent.getTimestamp();
}
+ index++;
}
if (lastStartTime != 0) {
resultList.add(AppUsagePeriod.newBuilder()
@@ -1180,43 +1148,22 @@
Context context,
final List<Long> rawTimestampList,
final List<Long> expectedTimestampSlots,
- final long currentTime,
- final boolean isFromFullCharge,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
final Map<Long, Map<String, BatteryHistEntry>> resultMap) {
if (rawTimestampList.isEmpty() || expectedTimestampSlots.isEmpty()) {
return;
}
- final long expectedStartTimestamp = expectedTimestampSlots.get(0);
- final long rawStartTimestamp = rawTimestampList.get(0);
- int startIndex = 0;
- // If the expected start timestamp is full charge or earlier than what we have, use the
- // first data of what we have directly. This should be OK because the expected start
- // timestamp is the nearest even hour of the raw start timestamp, their time diff is no
- // more than 1 hour.
- if (isFromFullCharge || expectedStartTimestamp < rawStartTimestamp) {
- startIndex = 1;
- resultMap.put(expectedStartTimestamp, batteryHistoryMap.get(rawStartTimestamp));
- }
final int expectedTimestampSlotsSize = expectedTimestampSlots.size();
- for (int index = startIndex; index < expectedTimestampSlotsSize; index++) {
- final long currentSlot = expectedTimestampSlots.get(index);
- if (currentSlot > currentTime) {
- // The slot timestamp is greater than the current time. Puts a placeholder first,
- // then in the async task, loads the real time battery usage data from the battery
- // stats service.
- // If current time is odd hour, one placeholder is added. If the current hour is
- // even hour, two placeholders are added. This is because the method
- // insertHourlyUsageDiffDataPerSlot() requires continuing three hours data.
- resultMap.put(currentSlot,
- Map.of(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER, EMPTY_BATTERY_HIST_ENTRY));
- continue;
- }
- final boolean isStartOrEnd = index == 0 || index == expectedTimestampSlotsSize - 1;
- interpolateHistoryForSlot(
- context, currentSlot, rawTimestampList, batteryHistoryMap, resultMap,
- isStartOrEnd);
+ final long startTimestamp = expectedTimestampSlots.get(0);
+ final long endTimestamp = expectedTimestampSlots.get(expectedTimestampSlotsSize - 1);
+
+ resultMap.put(startTimestamp, batteryHistoryMap.get(startTimestamp));
+ for (int index = 1; index < expectedTimestampSlotsSize - 1; index++) {
+ interpolateHistoryForSlot(context, expectedTimestampSlots.get(index), rawTimestampList,
+ batteryHistoryMap, resultMap);
}
+ resultMap.put(endTimestamp,
+ Map.of(CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER, EMPTY_BATTERY_HIST_ENTRY));
}
private static void interpolateHistoryForSlot(
@@ -1224,8 +1171,7 @@
final long currentSlot,
final List<Long> rawTimestampList,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
- final Map<Long, Map<String, BatteryHistEntry>> resultMap,
- final boolean isStartOrEnd) {
+ final Map<Long, Map<String, BatteryHistEntry>> resultMap) {
final long[] nearestTimestamps = findNearestTimestamp(rawTimestampList, currentSlot);
final long lowerTimestamp = nearestTimestamps[0];
final long upperTimestamp = nearestTimestamps[1];
@@ -1248,9 +1194,8 @@
resultMap.put(currentSlot, new ArrayMap<>());
return;
}
- interpolateHistoryForSlot(context,
- currentSlot, lowerTimestamp, upperTimestamp, batteryHistoryMap, resultMap,
- isStartOrEnd);
+ interpolateHistoryForSlot(
+ context, currentSlot, lowerTimestamp, upperTimestamp, batteryHistoryMap, resultMap);
}
private static void interpolateHistoryForSlot(
@@ -1259,8 +1204,7 @@
final long lowerTimestamp,
final long upperTimestamp,
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap,
- final Map<Long, Map<String, BatteryHistEntry>> resultMap,
- final boolean isStartOrEnd) {
+ final Map<Long, Map<String, BatteryHistEntry>> resultMap) {
final Map<String, BatteryHistEntry> lowerEntryDataMap =
batteryHistoryMap.get(lowerTimestamp);
final Map<String, BatteryHistEntry> upperEntryDataMap =
@@ -1274,7 +1218,7 @@
// Skips the booting-specific logics and always does interpolation for daily chart level
// data.
if (lowerTimestamp < upperEntryDataBootTimestamp
- && !isForDailyChart(isStartOrEnd, currentSlot)) {
+ && !TimestampUtils.isMidnight(currentSlot)) {
// Provides an opportunity to force align the slot directly.
if ((upperTimestamp - currentSlot) < 10 * DateUtils.MINUTE_IN_MILLIS) {
log(context, "force align into the nearest slot", currentSlot, null);
@@ -1321,70 +1265,6 @@
resultMap.put(currentSlot, newHistEntryMap);
}
- /**
- * @return Returns the nearest even hour timestamp of the given timestamp.
- */
- private static long getNearestEvenHourTimestamp(long rawTimestamp) {
- // If raw hour is even, the nearest even hour should be the even hour before raw
- // start. The hour doesn't need to change and just set the minutes and seconds to 0.
- // Otherwise, the nearest even hour should be raw hour + 1.
- // For example, the nearest hour of 14:30:50 should be 14:00:00. While the nearest
- // hour of 15:30:50 should be 16:00:00.
- return getEvenHourTimestamp(rawTimestamp, /*addHourOfDay*/ 1);
- }
-
- /**
- * @return Returns the fist even hour timestamp after the given timestamp.
- */
- private static long getFirstEvenHourAfterTimestamp(long rawTimestamp) {
- return getLastEvenHourBeforeTimestamp(rawTimestamp + DateUtils.HOUR_IN_MILLIS * 2);
- }
-
- /**
- * @return Returns the last even hour timestamp before the given timestamp.
- */
- private static long getLastEvenHourBeforeTimestamp(long rawTimestamp) {
- // If raw hour is even, the hour doesn't need to change as well.
- // Otherwise, the even hour before raw end should be raw hour - 1.
- // For example, the even hour before 14:30:50 should be 14:00:00. While the even
- // hour before 15:30:50 should be 14:00:00.
- return getEvenHourTimestamp(rawTimestamp, /*addHourOfDay*/ -1);
- }
-
- private static long getEvenHourTimestamp(long rawTimestamp, int addHourOfDay) {
- final Calendar evenHourCalendar = Calendar.getInstance();
- evenHourCalendar.setTimeInMillis(rawTimestamp);
- // Before computing the evenHourCalendar, record raw hour based on local timezone.
- final int rawHour = evenHourCalendar.get(Calendar.HOUR_OF_DAY);
- if (rawHour % 2 != 0) {
- evenHourCalendar.add(Calendar.HOUR_OF_DAY, addHourOfDay);
- }
- evenHourCalendar.set(Calendar.MINUTE, 0);
- evenHourCalendar.set(Calendar.SECOND, 0);
- evenHourCalendar.set(Calendar.MILLISECOND, 0);
- return evenHourCalendar.getTimeInMillis();
- }
-
- private static List<List<Long>> getHourlyTimestamps(final List<Long> dailyTimestamps) {
- final List<List<Long>> hourlyTimestamps = new ArrayList<>();
- if (dailyTimestamps.size() < MIN_DAILY_DATA_SIZE) {
- return hourlyTimestamps;
- }
-
- for (int dailyStartIndex = 0; dailyStartIndex < dailyTimestamps.size() - 1;
- dailyStartIndex++) {
- long currentTimestamp = dailyTimestamps.get(dailyStartIndex);
- final long dailyEndTimestamp = dailyTimestamps.get(dailyStartIndex + 1);
- final List<Long> hourlyTimestampsPerDay = new ArrayList<>();
- while (currentTimestamp <= dailyEndTimestamp) {
- hourlyTimestampsPerDay.add(currentTimestamp);
- currentTimestamp += MIN_TIME_SLOT;
- }
- hourlyTimestamps.add(hourlyTimestampsPerDay);
- }
- return hourlyTimestamps;
- }
-
private static List<BatteryLevelData.PeriodBatteryLevelData> getHourlyPeriodBatteryLevelData(
Context context,
final Map<Long, Map<String, BatteryHistEntry>> processedBatteryHistoryMap,
@@ -1461,11 +1341,16 @@
final Long endTimestamp = hourlyTimestamps.get(hourlyIndex + 1);
final long slotDuration = endTimestamp - startTimestamp;
List<Map<String, BatteryHistEntry>> slotBatteryHistoryList = new ArrayList<>();
- for (Long timestamp = startTimestamp; timestamp <= endTimestamp;
- timestamp += DateUtils.HOUR_IN_MILLIS) {
+ slotBatteryHistoryList.add(
+ batteryHistoryMap.getOrDefault(startTimestamp, EMPTY_BATTERY_MAP));
+ for (Long timestamp = TimestampUtils.getNextHourTimestamp(startTimestamp);
+ timestamp < endTimestamp; timestamp += DateUtils.HOUR_IN_MILLIS) {
slotBatteryHistoryList.add(
batteryHistoryMap.getOrDefault(timestamp, EMPTY_BATTERY_MAP));
}
+ slotBatteryHistoryList.add(
+ batteryHistoryMap.getOrDefault(endTimestamp, EMPTY_BATTERY_MAP));
+
final BatteryDiffData hourlyBatteryDiffData =
insertHourlyUsageDiffDataPerSlot(
context,
@@ -1938,16 +1823,6 @@
return true;
}
- private static long getTimestampWithDayDiff(final long timestamp, final int dayDiff) {
- final Calendar calendar = Calendar.getInstance();
- calendar.setTimeInMillis(timestamp);
- calendar.add(Calendar.DAY_OF_YEAR, dayDiff);
- calendar.set(Calendar.HOUR_OF_DAY, 0);
- calendar.set(Calendar.MINUTE, 0);
- calendar.set(Calendar.SECOND, 0);
- return calendar.getTimeInMillis();
- }
-
private static long getDiffValue(long v1, long v2) {
return v2 > v1 ? v2 - v1 : 0;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 84a39bb..0435e45 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -110,13 +110,7 @@
// For testing only.
@VisibleForTesting
- static Supplier<Cursor> sFakeBatteryStateSupplier;
- @VisibleForTesting
- static Supplier<Cursor> sFakeAppUsageEventSupplier;
- @VisibleForTesting
- static Supplier<Cursor> sFakeAppUsageLatestTimestampSupplier;
- @VisibleForTesting
- static Supplier<Cursor> sFakeBatteryEventSupplier;
+ static Supplier<Cursor> sFakeSupplier;
private DatabaseUtils() {
}
@@ -340,7 +334,7 @@
resolver.insert(BATTERY_EVENT_URI, contentValues);
Log.d(TAG, "insert() battery event data into database: " + batteryEvent.toString());
} catch (Exception e) {
- Log.e(TAG, "insert() battery event data into database error:\n" + e);
+ Log.e(TAG, "insert() battery event data into database error:", e);
}
Log.d(TAG, String.format("sendBatteryEventData() in %d/ms",
(System.currentTimeMillis() - startTime)));
@@ -487,8 +481,8 @@
Context context, final Uri appUsageLatestTimestampUri) {
// We have already make sure the context here is with profile parent's user identity. Don't
// need to check whether current user is work profile.
- try (Cursor cursor = sFakeAppUsageLatestTimestampSupplier != null
- ? sFakeAppUsageLatestTimestampSupplier.get()
+ try (Cursor cursor = sFakeSupplier != null
+ ? sFakeSupplier.get()
: context.getContentResolver().query(
appUsageLatestTimestampUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
@@ -514,8 +508,8 @@
if (context == null) {
return appUsageEventList;
}
- try (Cursor cursor = sFakeAppUsageEventSupplier != null
- ? sFakeAppUsageEventSupplier.get()
+ try (Cursor cursor = sFakeSupplier != null
+ ? sFakeSupplier.get()
: context.getContentResolver().query(appUsageEventUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return appUsageEventList;
@@ -540,8 +534,8 @@
if (context == null) {
return batteryEventList;
}
- try (Cursor cursor = sFakeBatteryEventSupplier != null
- ? sFakeBatteryEventSupplier.get()
+ try (Cursor cursor = sFakeSupplier != null
+ ? sFakeSupplier.get()
: context.getContentResolver().query(batteryEventUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return batteryEventList;
@@ -566,7 +560,7 @@
return null;
}
final Map<Long, Map<String, BatteryHistEntry>> resultMap = new HashMap();
- try (Cursor cursor = sFakeBatteryStateSupplier != null ? sFakeBatteryStateSupplier.get() :
+ try (Cursor cursor = sFakeSupplier != null ? sFakeSupplier.get() :
context.getContentResolver().query(batteryStateUri, null, null, null)) {
if (cursor == null || cursor.getCount() == 0) {
return resultMap;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java b/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java
new file mode 100644
index 0000000..594a0ef
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import java.util.Calendar;
+
+/** A utility class for timestamp operations. */
+final class TimestampUtils {
+
+ static long getNextHourTimestamp(final long timestamp) {
+ final Calendar calendar = getSharpHourCalendar(timestamp);
+ calendar.add(Calendar.HOUR_OF_DAY, 1);
+ return calendar.getTimeInMillis();
+ }
+
+ static long getNextEvenHourTimestamp(final long timestamp) {
+ final Calendar calendar = getSharpHourCalendar(timestamp);
+ final int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ calendar.add(Calendar.HOUR_OF_DAY, hour % 2 == 0 ? 2 : 1);
+ return calendar.getTimeInMillis();
+ }
+
+ static long getLastEvenHourTimestamp(final long timestamp) {
+ final Calendar calendar = getSharpHourCalendar(timestamp);
+ final int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ calendar.add(Calendar.HOUR_OF_DAY, hour % 2 == 0 ? 0 : -1);
+ return calendar.getTimeInMillis();
+ }
+
+ static long getNextDayTimestamp(final long timestamp) {
+ final Calendar calendar = getSharpHourCalendar(timestamp);
+ calendar.add(Calendar.DAY_OF_YEAR, 1);
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ return calendar.getTimeInMillis();
+ }
+
+ static boolean isMidnight(final long timestamp) {
+ final Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(timestamp);
+ return calendar.get(Calendar.HOUR_OF_DAY) == 0
+ && calendar.get(Calendar.MINUTE) == 0
+ && calendar.get(Calendar.SECOND) == 0
+ && calendar.get(Calendar.MILLISECOND) == 0;
+ }
+
+ private static Calendar getSharpHourCalendar(final long timestamp) {
+ final Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(timestamp);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ return calendar;
+ }
+}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
index f1d7e7f..14578af 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPickerDialogFragment.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
@@ -52,11 +53,13 @@
public class ModifierKeysPickerDialogFragment extends DialogFragment {
+ static final String DEFAULT_KEY = "default_key";
+ static final String SELECTION_KEY = "delection_key";
+
private Preference mPreference;
private String mKeyDefaultName;
private String mKeyFocus;
- private Context mContext;
- private InputManager mIm;
+ private Activity mActivity;
private List<int[]> mRemappableKeyList =
new ArrayList<>(Arrays.asList(
@@ -67,36 +70,41 @@
private Map<String, int[]> mRemappableKeyMap = new HashMap<>();
- public ModifierKeysPickerDialogFragment() {
- }
+ public ModifierKeysPickerDialogFragment() {}
- public ModifierKeysPickerDialogFragment(Preference preference, InputManager inputManager) {
- mPreference = preference;
- mKeyDefaultName = preference.getTitle().toString();
- mKeyFocus = preference.getSummary().toString();
- mIm = inputManager;
+ @Override
+ public void onSaveInstanceState(Bundle savedInstanceState) {
+ savedInstanceState.putString(SELECTION_KEY, mKeyFocus);
+ super.onSaveInstanceState(savedInstanceState);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
- mContext = getActivity();
+
+ mActivity = getActivity();
+ InputManager inputManager = mActivity.getSystemService(InputManager.class);
+ mKeyDefaultName = getArguments().getString(DEFAULT_KEY);
+ mKeyFocus = getArguments().getString(SELECTION_KEY);
+ if (savedInstanceState != null) {
+ mKeyFocus = savedInstanceState.getString(SELECTION_KEY);
+ }
List<String> modifierKeys = new ArrayList<String>(Arrays.asList(
- mContext.getString(R.string.modifier_keys_caps_lock),
- mContext.getString(R.string.modifier_keys_ctrl),
- mContext.getString(R.string.modifier_keys_meta),
- mContext.getString(R.string.modifier_keys_alt)));
+ mActivity.getString(R.string.modifier_keys_caps_lock),
+ mActivity.getString(R.string.modifier_keys_ctrl),
+ mActivity.getString(R.string.modifier_keys_meta),
+ mActivity.getString(R.string.modifier_keys_alt)));
for (int i = 0; i < modifierKeys.size(); i++) {
mRemappableKeyMap.put(modifierKeys.get(i), mRemappableKeyList.get(i));
}
View dialoglayout =
- LayoutInflater.from(mContext).inflate(R.layout.modifier_key_picker_dialog, null);
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
+ LayoutInflater.from(mActivity).inflate(R.layout.modifier_key_picker_dialog, null);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mActivity);
dialogBuilder.setView(dialoglayout);
TextView summary = dialoglayout.findViewById(R.id.modifier_key_picker_summary);
- CharSequence summaryText = mContext.getString(
+ CharSequence summaryText = mActivity.getString(
R.string.modifier_keys_picker_summary, mKeyDefaultName);
summary.setText(summaryText);
@@ -119,14 +127,14 @@
Spannable itemSummary;
if (selectedItem.equals(mKeyDefaultName)) {
itemSummary = new SpannableString(
- mContext.getString(R.string.modifier_keys_default_summary));
+ mActivity.getString(R.string.modifier_keys_default_summary));
itemSummary.setSpan(
new ForegroundColorSpan(getColorOfTextColorSecondary()),
0, itemSummary.length(), 0);
// Set keys to default.
int[] keys = mRemappableKeyMap.get(mKeyDefaultName);
for (int i = 0; i < keys.length; i++) {
- mIm.remapModifierKey(keys[i], keys[i]);
+ inputManager.remapModifierKey(keys[i], keys[i]);
}
} else {
itemSummary = new SpannableString(selectedItem);
@@ -136,29 +144,29 @@
int[] fromKeys = mRemappableKeyMap.get(mKeyDefaultName);
int[] toKeys = mRemappableKeyMap.get(selectedItem);
// CAPS_LOCK only one key, so always choose the left key for remapping.
- if (isKeyCapsLock(mContext, mKeyDefaultName)) {
- mIm.remapModifierKey(fromKeys[0], toKeys[0]);
+ if (isKeyCapsLock(mActivity, mKeyDefaultName)) {
+ inputManager.remapModifierKey(fromKeys[0], toKeys[0]);
}
// Remap KEY_LEFT and KEY_RIGHT to CAPS_LOCK.
- if (!isKeyCapsLock(mContext, mKeyDefaultName)
- && isKeyCapsLock(mContext, selectedItem)) {
- mIm.remapModifierKey(fromKeys[0], toKeys[0]);
- mIm.remapModifierKey(fromKeys[1], toKeys[0]);
+ if (!isKeyCapsLock(mActivity, mKeyDefaultName)
+ && isKeyCapsLock(mActivity, selectedItem)) {
+ inputManager.remapModifierKey(fromKeys[0], toKeys[0]);
+ inputManager.remapModifierKey(fromKeys[1], toKeys[0]);
}
// Auto handle left and right keys remapping.
- if (!isKeyCapsLock(mContext, mKeyDefaultName)
- && !isKeyCapsLock(mContext, selectedItem)) {
- mIm.remapModifierKey(fromKeys[0], toKeys[0]);
- mIm.remapModifierKey(fromKeys[1], toKeys[1]);
+ if (!isKeyCapsLock(mActivity, mKeyDefaultName)
+ && !isKeyCapsLock(mActivity, selectedItem)) {
+ inputManager.remapModifierKey(fromKeys[0], toKeys[0]);
+ inputManager.remapModifierKey(fromKeys[1], toKeys[1]);
}
}
- mPreference.setSummary(itemSummary);
- modifierKeyDialog.dismiss();
+ dismiss();
+ mActivity.recreate();
});
Button cancelButton = dialoglayout.findViewById(R.id.modifier_key_cancel_button);
cancelButton.setOnClickListener(v -> {
- modifierKeyDialog.dismiss();
+ dismiss();
});
final Window window = modifierKeyDialog.getWindow();
@@ -207,16 +215,17 @@
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if (view == null) {
- view = LayoutInflater.from(mContext).inflate(R.layout.modifier_key_item, null);
+ view = LayoutInflater.from(mActivity).inflate(R.layout.modifier_key_item, null);
}
TextView textView = view.findViewById(R.id.modifier_key_text);
ImageView checkIcon = view.findViewById(R.id.modifier_key_check_icon);
textView.setText(mList.get(i));
if (mCurrentItem == i) {
+ mKeyFocus = mList.get(i);
textView.setTextColor(getColorOfColorAccentPrimaryVariant());
checkIcon.setImageAlpha(255);
view.setBackground(
- mContext.getDrawable(R.drawable.modifier_key_lisetview_background));
+ mActivity.getDrawable(R.drawable.modifier_key_lisetview_background));
} else {
textView.setTextColor(getColorOfTextColorPrimary());
checkIcon.setImageAlpha(0);
@@ -235,15 +244,15 @@
}
private int getColorOfTextColorPrimary() {
- return Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary);
+ return Utils.getColorAttrDefaultColor(mActivity, android.R.attr.textColorPrimary);
}
private int getColorOfTextColorSecondary() {
- return Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorSecondary);
+ return Utils.getColorAttrDefaultColor(mActivity, android.R.attr.textColorSecondary);
}
private int getColorOfColorAccentPrimaryVariant() {
return Utils.getColorAttrDefaultColor(
- mContext, com.android.internal.R.attr.materialColorPrimaryContainer);
+ mActivity, com.android.internal.R.attr.materialColorPrimaryContainer);
}
}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
index 780a980..91caf29 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java
@@ -18,12 +18,12 @@
import android.content.Context;
import android.hardware.input.InputManager;
+import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.view.KeyEvent;
-import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
@@ -41,8 +41,8 @@
public class ModifierKeysPreferenceController extends BasePreferenceController {
- private static String KEY_TAG = "modifier_keys_dialog_tag";
- private static String KEY_RESTORE_PREFERENCE = "modifier_keys_restore";
+ private static final String KEY_TAG = "modifier_keys_dialog_tag";
+ private static final String KEY_RESTORE_PREFERENCE = "modifier_keys_restore";
private static final String KEY_PREFERENCE_CAPS_LOCK = "modifier_keys_caps_lock";
private static final String KEY_PREFERENCE_CTRL = "modifier_keys_ctrl";
@@ -52,6 +52,7 @@
private Fragment mParent;
private FragmentManager mFragmentManager;
private final InputManager mIm;
+ private PreferenceScreen mScreen;
private final List<Integer> mRemappableKeys = new ArrayList<>(
Arrays.asList(
@@ -82,40 +83,39 @@
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
-
if (mParent == null) {
return;
}
+ mScreen = screen;
+ refreshUi();
+ }
+ private void refreshUi() {
for (Map.Entry<Integer, Integer> entry : mIm.getModifierKeyRemapping().entrySet()) {
int fromKey = entry.getKey();
int toKey = entry.getValue();
int index = mRemappableKeys.indexOf(toKey);
if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = screen.findPreference(KEY_PREFERENCE_CTRL);
+ Preference preference = mScreen.findPreference(KEY_PREFERENCE_CTRL);
preference.setSummary(changeSummaryColor(mKeyNames[index]));
}
if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = screen.findPreference(KEY_PREFERENCE_META);
+ Preference preference = mScreen.findPreference(KEY_PREFERENCE_META);
preference.setSummary(changeSummaryColor(mKeyNames[index]));
}
if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = screen.findPreference(KEY_PREFERENCE_ALT);
+ Preference preference = mScreen.findPreference(KEY_PREFERENCE_ALT);
preference.setSummary(changeSummaryColor(mKeyNames[index]));
}
if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) {
- Preference preference = screen.findPreference(KEY_PREFERENCE_CAPS_LOCK);
+ Preference preference = mScreen.findPreference(KEY_PREFERENCE_CAPS_LOCK);
preference.setSummary(changeSummaryColor(mKeyNames[index]));
}
}
-
- // The dialog screen depends on the previous selected key's fragment.
- // In the rotation scenario, we should remove the previous dialog screen first.
- clearPreviousDialog();
}
@Override
@@ -133,19 +133,18 @@
}
private void showModifierKeysDialog(Preference preference) {
- ModifierKeysPickerDialogFragment fragment =
- new ModifierKeysPickerDialogFragment(preference, mIm);
- fragment.setTargetFragment(mParent, 0);
- fragment.show(mFragmentManager, KEY_TAG);
- }
-
- private void clearPreviousDialog() {
mFragmentManager = mParent.getFragmentManager();
- DialogFragment preKeysDialogFragment =
- (DialogFragment) mFragmentManager.findFragmentByTag(KEY_TAG);
- if (preKeysDialogFragment != null) {
- preKeysDialogFragment.dismiss();
- }
+ ModifierKeysPickerDialogFragment fragment = new ModifierKeysPickerDialogFragment();
+ fragment.setTargetFragment(mParent, 0);
+ Bundle bundle = new Bundle();
+ bundle.putString(
+ ModifierKeysPickerDialogFragment.DEFAULT_KEY,
+ preference.getTitle().toString());
+ bundle.putString(
+ ModifierKeysPickerDialogFragment.SELECTION_KEY,
+ preference.getSummary().toString());
+ fragment.setArguments(bundle);
+ fragment.show(mFragmentManager, KEY_TAG);
}
private Spannable changeSummaryColor(String summary) {
diff --git a/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java b/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java
index 4ca5ddd..755e9dd 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java
@@ -18,25 +18,19 @@
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Bundle;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import androidx.fragment.app.DialogFragment;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
import com.android.settings.R;
-import com.android.settingslib.Utils;
public class ModifierKeysResetDialogFragment extends DialogFragment {
private static final String MODIFIER_KEYS_CAPS_LOCK = "modifier_keys_caps_lock";
@@ -44,41 +38,36 @@
private static final String MODIFIER_KEYS_META = "modifier_keys_meta";
private static final String MODIFIER_KEYS_ALT = "modifier_keys_alt";
- private PreferenceScreen mScreen;
- private InputManager mIm;
private String[] mKeys = {
MODIFIER_KEYS_CAPS_LOCK,
MODIFIER_KEYS_CTRL,
MODIFIER_KEYS_META,
MODIFIER_KEYS_ALT};
- public ModifierKeysResetDialogFragment() {
- }
-
- public ModifierKeysResetDialogFragment(PreferenceScreen screen, InputManager inputManager) {
- mScreen = screen;
- mIm = inputManager;
- }
+ public ModifierKeysResetDialogFragment() {}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
- Context mContext = getActivity();
+
+ Activity activity = getActivity();
+ InputManager inputManager = activity.getSystemService(InputManager.class);
View dialoglayout =
- LayoutInflater.from(mContext).inflate(R.layout.modifier_key_reset_dialog, null);
- AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
+ LayoutInflater.from(activity).inflate(R.layout.modifier_key_reset_dialog, null);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
dialogBuilder.setView(dialoglayout);
AlertDialog modifierKeyResetDialog = dialogBuilder.create();
Button restoreButton = dialoglayout.findViewById(R.id.modifier_key_reset_restore_button);
restoreButton.setOnClickListener(v -> {
- resetToDefault();
- modifierKeyResetDialog.dismiss();
+ inputManager.clearAllModifierKeyRemappings();
+ dismiss();
+ activity.recreate();
});
Button cancelButton = dialoglayout.findViewById(R.id.modifier_key_reset_cancel_button);
cancelButton.setOnClickListener(v -> {
- modifierKeyResetDialog.dismiss();
+ dismiss();
});
final Window window = modifierKeyResetDialog.getWindow();
@@ -86,25 +75,4 @@
return modifierKeyResetDialog;
}
-
- private void resetToDefault() {
- Context mContext = getActivity();
- for (int i = 0; i < mKeys.length; i++) {
- Preference preference = mScreen.findPreference(mKeys[i]);
- Spannable title = new SpannableString(
- mContext.getString(R.string.modifier_keys_default_summary));
- title.setSpan(
- new ForegroundColorSpan(getColorOfTextColorSecondary()),
- 0, title.length(), 0);
- preference.setSummary(title);
- }
-
- if (mIm != null) {
- mIm.clearAllModifierKeyRemappings();
- }
- }
-
- private int getColorOfTextColorSecondary() {
- return Utils.getColorAttrDefaultColor(getActivity(), android.R.attr.textColorSecondary);
- }
}
diff --git a/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java
index 4e72fbd..3171487 100644
--- a/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java
+++ b/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java
@@ -17,12 +17,10 @@
package com.android.settings.inputmethod;
import android.content.Context;
-import android.hardware.input.InputManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
-import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
@@ -34,16 +32,14 @@
public class ModifierKeysRestorePreferenceController extends BasePreferenceController {
- private static String KEY_TAG = "modifier_keys_dialog_tag";
+ private static final String KEY_TAG = "modifier_keys_restore_dialog_tag";
private Fragment mParent;
private FragmentManager mFragmentManager;
private PreferenceScreen mScreen;
- private final InputManager mIm;
public ModifierKeysRestorePreferenceController(Context context, String key) {
super(context, key);
- mIm = context.getSystemService(InputManager.class);
}
public void setFragment(Fragment parent) {
@@ -57,9 +53,6 @@
return;
}
mScreen = screen;
- // The dialog screen depends on the previous selected key's fragment.
- // In the rotation scenario, we should remove the previous dialog first.
- clearPreviousDialog();
setResetKeyColor();
}
@@ -78,8 +71,8 @@
}
private void showResetDialog() {
- ModifierKeysResetDialogFragment fragment =
- new ModifierKeysResetDialogFragment(mScreen, mIm);
+ mFragmentManager = mParent.getFragmentManager();
+ ModifierKeysResetDialogFragment fragment = new ModifierKeysResetDialogFragment();
fragment.setTargetFragment(mParent, 0);
fragment.show(mFragmentManager, KEY_TAG);
}
@@ -98,13 +91,4 @@
return Utils.getColorAttrDefaultColor(
mParent.getActivity(), com.android.internal.R.attr.materialColorPrimaryContainer);
}
-
- private void clearPreviousDialog() {
- mFragmentManager = mParent.getFragmentManager();
- DialogFragment preResetDialogFragment =
- (DialogFragment) mFragmentManager.findFragmentByTag(KEY_TAG);
- if (preResetDialogFragment != null) {
- preResetDialogFragment.dismiss();
- }
- }
}
diff --git a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
index 05c7401..a639c9d 100644
--- a/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
+++ b/src/com/android/settings/localepicker/LocaleHelperPreferenceController.java
@@ -17,6 +17,8 @@
package com.android.settings.localepicker;
import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
@@ -65,10 +67,14 @@
}
private void openLocaleLearnMoreLink() {
- mContext.startActivity(
- HelpUtils.getHelpIntent(
- mContext,
- mContext.getString(R.string.link_locale_picker_footer_learn_more),
- /*backupContext=*/""));
+ Intent intent = HelpUtils.getHelpIntent(
+ mContext,
+ mContext.getString(R.string.link_locale_picker_footer_learn_more),
+ mContext.getClass().getName());
+ if (intent != null) {
+ mContext.startActivity(intent);
+ } else {
+ Log.w(TAG, "HelpIntent is null");
+ }
}
}
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 9c4ac03..9d953bf 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -438,6 +438,7 @@
}
}
+ Log.d(TAG, "getSelectableSubscriptionInfoList: " + selectableList);
return selectableList;
}
}
diff --git a/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
new file mode 100644
index 0000000..b984622
--- /dev/null
+++ b/src/com/android/settings/notetask/shortcut/CreateNoteTaskShortcutActivity.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2023 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.notetask.shortcut
+
+import android.app.Activity
+import android.app.role.RoleManager
+import android.app.role.RoleManager.ROLE_NOTES
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ShortcutInfo
+import android.content.pm.ShortcutManager
+import android.graphics.drawable.Icon
+import android.os.Bundle
+import android.os.PersistableBundle
+import android.os.UserHandle
+import androidx.activity.ComponentActivity
+import androidx.core.content.getSystemService
+import com.android.settings.R
+
+/**
+ * Activity responsible for create a shortcut for notes action. If the shortcut is enabled, a new
+ * shortcut will appear in the widget picker. If the shortcut is selected, the Activity here will be
+ * launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish.
+ *
+ * @see <a
+ * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating
+ * a custom shortcut activity</a>
+ */
+internal class CreateNoteTaskShortcutActivity : ComponentActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ val roleManager = requireNotNull(getSystemService<RoleManager>())
+ val shortcutManager = requireNotNull(getSystemService<ShortcutManager>())
+
+ super.onCreate(savedInstanceState)
+
+ val shortcutInfo = roleManager.createNoteShortcutInfoAsUser(context = this, user)
+ val shortcutIntent = shortcutManager.createShortcutResultIntent(shortcutInfo)
+ setResult(Activity.RESULT_OK, shortcutIntent)
+
+ finish()
+ }
+
+ private companion object {
+
+ private const val SHORTCUT_ID = "note_task_shortcut_id"
+ private const val EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE =
+ "extra_shortcut_badge_override_package"
+ private const val ACTION_LAUNCH_NOTE_TASK = "com.android.systemui.action.LAUNCH_NOTE_TASK"
+
+ private fun RoleManager.createNoteShortcutInfoAsUser(
+ context: Context,
+ user: UserHandle,
+ ): ShortcutInfo? {
+ val systemUiComponent = context.getSystemUiComponent() ?: return null
+
+ val extras = PersistableBundle()
+ getDefaultRoleHolderAsUser(ROLE_NOTES, user)?.let { packageName ->
+ // Set custom app badge using the icon from ROLES_NOTES default app.
+ extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, packageName)
+ }
+
+ val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget)
+
+ val intent = Intent(ACTION_LAUNCH_NOTE_TASK).apply {
+ setPackage(systemUiComponent.packageName)
+ }
+
+ return ShortcutInfo.Builder(context, SHORTCUT_ID)
+ .setIntent(intent)
+ .setShortLabel(context.getString(R.string.note_task_button_label))
+ .setLongLived(true)
+ .setIcon(icon)
+ .setExtras(extras)
+ .build()
+ }
+
+ private fun RoleManager.getDefaultRoleHolderAsUser(
+ role: String,
+ user: UserHandle,
+ ): String? = getRoleHoldersAsUser(role, user).firstOrNull()
+
+ private fun Context.getSystemUiComponent(): ComponentName? {
+ val flattenName = getString(
+ com.android.internal.R.string.config_systemUIServiceComponent)
+ check(flattenName.isNotEmpty()) {
+ "No 'com.android.internal.R.string.config_systemUIServiceComponent' resource"
+ }
+ return try {
+ ComponentName.unflattenFromString(flattenName)
+ } catch (e: RuntimeException) {
+ val message = "Invalid component name defined by 'com.android.internal.R.string." +
+ "config_systemUIServiceComponent' resource: $flattenName"
+ throw IllegalStateException(message, e)
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/panel/PanelFeatureProviderImpl.java b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
index 57a1ab0..71711f9 100644
--- a/src/com/android/settings/panel/PanelFeatureProviderImpl.java
+++ b/src/com/android/settings/panel/PanelFeatureProviderImpl.java
@@ -22,9 +22,9 @@
import android.provider.Settings;
import android.util.FeatureFlagUtils;
-public class PanelFeatureProviderImpl implements PanelFeatureProvider {
+import com.android.settings.Utils;
- private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
+public class PanelFeatureProviderImpl implements PanelFeatureProvider {
@Override
public PanelContent getPanel(Context context, Bundle bundle) {
@@ -42,7 +42,7 @@
// Redirect to the internet dialog in SystemUI.
Intent intent = new Intent(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .setPackage(SYSTEMUI_PACKAGE_NAME);
+ .setPackage(Utils.SYSTEMUI_PACKAGE_NAME);
context.sendBroadcast(intent);
return null;
case Settings.Panel.ACTION_NFC:
@@ -55,7 +55,7 @@
// Redirect to the volume panel in SystemUI.
Intent volumeIntent = new Intent(Settings.Panel.ACTION_VOLUME);
volumeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND).setPackage(
- SYSTEMUI_PACKAGE_NAME);
+ Utils.SYSTEMUI_PACKAGE_NAME);
context.sendBroadcast(volumeIntent);
return null;
} else {
diff --git a/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceController.java
new file mode 100644
index 0000000..6ba9d18
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceController.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2023 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.regionalpreferences;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.HelpUtils;
+import com.android.settingslib.widget.FooterPreference;
+
+/**
+ * Preference controller for regional preference footer.
+ */
+public class RegionalFooterPreferenceController extends BasePreferenceController {
+
+ private static final String TAG = "RegionalFooterPreferenceController";
+
+ public RegionalFooterPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE_UNSEARCHABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ FooterPreference footerPreference = screen.findPreference(getPreferenceKey());
+ setupFooterPreference(footerPreference);
+ }
+
+ @VisibleForTesting
+ void setupFooterPreference(FooterPreference footerPreference) {
+ if (footerPreference != null) {
+ footerPreference.setLearnMoreAction(v -> openLocaleLearnMoreLink());
+ footerPreference.setLearnMoreText(mContext.getString(
+ R.string.desc_regional_pref_footer_learn_more));
+ }
+ }
+
+ private void openLocaleLearnMoreLink() {
+ Intent intent = HelpUtils.getHelpIntent(
+ mContext,
+ mContext.getString(R.string.regional_pref_footer_learn_more_link),
+ mContext.getClass().getName());
+ if (intent != null) {
+ mContext.startActivity(intent);
+ } else {
+ Log.w(TAG, "HelpIntent is null");
+ }
+ }
+}
diff --git a/src/com/android/settings/safetycenter/BiometricsSafetySource.java b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
index 1fe366d..74964ce 100644
--- a/src/com/android/settings/safetycenter/BiometricsSafetySource.java
+++ b/src/com/android/settings/safetycenter/BiometricsSafetySource.java
@@ -85,7 +85,7 @@
combinedBiometricStatusUtils.getSummary(),
createPendingIntent(context,
biometricNavigationUtils.getBiometricSettingsIntent(context,
- combinedBiometricStatusUtils.getSettingsClassName(),
+ combinedBiometricStatusUtils.getSettingsClassNameBasedOnUser(),
disablingAdmin, Bundle.EMPTY),
REQUEST_CODE_COMBINED_BIOMETRIC_SETTING),
disablingAdmin == null /* enabled */,
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
index f6c9cc4..dabb088 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
@@ -43,7 +43,6 @@
import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -100,6 +99,9 @@
int lastRemovableSlotState = getLastRemovableSimSlotState(mContext);
int currentRemovableSlotState = removableSlotInfo.getCardStateInfo();
+ Log.i(TAG,
+ "lastRemovableSlotState: " + lastRemovableSlotState + ",currentRemovableSlotState: "
+ + currentRemovableSlotState);
boolean isRemovableSimInserted =
lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
&& currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT;
@@ -259,7 +261,7 @@
}
List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
- if (subscriptionInfos == null || subscriptionInfos.get(0) == null) {
+ if (subscriptionInfos.isEmpty()) {
Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
return;
}
@@ -275,6 +277,7 @@
private void setRemovableSimSlotState(Context context, int state) {
final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply();
+ Log.d(TAG, "setRemovableSimSlotState: " + state);
}
private int getSuwRemovableSlotAction(Context context) {
@@ -332,13 +335,14 @@
}
protected List<SubscriptionInfo> getAvailableRemovableSubscription() {
- List<SubscriptionInfo> subList = new ArrayList<>();
- for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(mContext)) {
- if (!info.isEmbedded()) {
- subList.add(info);
- }
- }
- return subList;
+ List<SubscriptionInfo> removableSubscriptions =
+ SubscriptionUtil.getAvailableSubscriptions(mContext);
+ return ImmutableList.copyOf(
+ removableSubscriptions.stream()
+ // ToDo: This condition is for psim only. If device supports removable
+ // esim, it needs an new condition.
+ .filter(sub -> !sub.isEmbedded())
+ .collect(Collectors.toList()));
}
private void startChooseSimActivity(boolean psimInserted) {
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index c2716b6..455fe9f 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -17,6 +17,7 @@
package com.android.settings.spa
import android.content.Context
+import android.util.FeatureFlagUtils
import com.android.settings.spa.app.AllAppListPageProvider
import com.android.settings.spa.app.AppsMainPageProvider
import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
@@ -43,6 +44,7 @@
import com.android.settings.spa.system.SystemMainPageProvider
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.framework.common.SpaLogger
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListTemplate
@@ -88,5 +90,8 @@
),
)
}
- override val logger = SpaLogProvider
+ override val logger =
+ if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_ENABLE_SPA_METRICS))
+ SpaLogProvider
+ else object: SpaLogger {}
}
diff --git a/src/com/android/settings/spa/system/AppLanguagesListModel.kt b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
index 3141d68..942bcc4 100644
--- a/src/com/android/settings/spa/system/AppLanguagesListModel.kt
+++ b/src/com/android/settings/spa/system/AppLanguagesListModel.kt
@@ -33,8 +33,10 @@
import com.android.settings.applications.appinfo.AppLocaleDetails
import com.android.settings.localepicker.AppLocalePickerActivity
import com.android.settingslib.spa.framework.util.filterItem
+import com.android.settingslib.spaprivileged.framework.common.asUser
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userHandle
import com.android.settingslib.spaprivileged.template.app.AppListItem
import com.android.settingslib.spaprivileged.template.app.AppListItemModel
import kotlinx.coroutines.Dispatchers
@@ -54,17 +56,18 @@
override fun transform(userIdFlow: Flow<Int>, appListFlow: Flow<List<ApplicationInfo>>) =
userIdFlow.map { userId ->
- packageManager.queryIntentActivitiesAsUser(
+ userId to packageManager.queryIntentActivitiesAsUser(
AppLocaleUtil.LAUNCHER_ENTRY_INTENT,
PackageManager.ResolveInfoFlags.of(PackageManager.GET_META_DATA.toLong()),
userId,
)
- }.combine(appListFlow) { resolveInfos, appList ->
+ }.combine(appListFlow) { (userId, resolveInfos), appList ->
+ val userContext = context.asUser(UserHandle.of(userId))
appList.map { app ->
AppLanguagesRecord(
app = app,
isAppLocaleSupported = AppLocaleUtil.canDisplayLocaleUi(
- context, app, resolveInfos
+ userContext, app, resolveInfos
),
)
}
@@ -86,9 +89,7 @@
}.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder))
private fun getSummary(app: ApplicationInfo): String =
- AppLocaleDetails.getAppDefaultLocale(context, app.packageName)?.let {
- AppLocaleDetails.getSummary(context, app).toString()
- } ?: context.getString(R.string.preference_of_system_locale_summary)
+ AppLocaleDetails.getSummary(context, app).toString()
@Composable
override fun AppListItemModel<AppLanguagesRecord>.AppItem() {
@@ -96,8 +97,7 @@
val intent = Intent(context, AppLocalePickerActivity::class.java).apply {
data = Uri.parse("package:${record.app.packageName}")
}
- val userHandle : UserHandle = UserHandle.getUserHandleForUid(record.app.uid)
- context.startActivityAsUser(intent, userHandle)
+ context.startActivityAsUser(intent, record.app.userHandle)
}
}
}
diff --git a/src/com/android/settings/support/SupportDashboardActivity.java b/src/com/android/settings/support/SupportDashboardActivity.java
index b8a22b9..4654858 100644
--- a/src/com/android/settings/support/SupportDashboardActivity.java
+++ b/src/com/android/settings/support/SupportDashboardActivity.java
@@ -17,7 +17,6 @@
import android.app.Activity;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import com.android.settings.R;
@@ -37,6 +36,9 @@
@SearchIndexable
public class SupportDashboardActivity extends Activity implements Indexable {
+ public static final String ACTION_SUPPORT_SETTINGS =
+ "com.android.settings.action.SUPPORT_SETTINGS";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -70,7 +72,7 @@
data.summaryOn = context.getString(R.string.support_summary);
data.intentTargetPackage = context.getPackageName();
data.intentTargetClass = SupportDashboardActivity.class.getName();
- data.intentAction = Intent.ACTION_MAIN;
+ data.intentAction = ACTION_SUPPORT_SETTINGS;
data.key = SUPPORT_SEARCH_INDEX_KEY;
result.add(data);
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index be2a68e..036487d 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -653,10 +653,14 @@
profile.ipsecSecret = mIpsecSecret.getText().toString();
break;
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ if (mIpsecUserCert.getSelectedItemPosition() != 0) {
+ profile.ipsecSecret = (String) mIpsecUserCert.getSelectedItem();
+ }
+ // fall through
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
profile.l2tpSecret = mL2tpSecret.getText().toString();
// fall through
- case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
if (mIpsecUserCert.getSelectedItemPosition() != 0) {
profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem();
diff --git a/src/com/android/settings/wifi/WifiConfigController2.java b/src/com/android/settings/wifi/WifiConfigController2.java
index 9a29c1c..f92b58f 100644
--- a/src/com/android/settings/wifi/WifiConfigController2.java
+++ b/src/com/android/settings/wifi/WifiConfigController2.java
@@ -592,28 +592,32 @@
return null;
}
- WifiConfiguration config = new WifiConfiguration();
-
+ WifiConfiguration config;
if (mWifiEntry == null) {
+ config = new WifiConfiguration();
config.SSID = "\"" + mSsidView.getText().toString() + "\"";
// If the user adds a network manually, assume that it is hidden.
config.hiddenSSID = mHiddenSettingsSpinner.getSelectedItemPosition() == HIDDEN_NETWORK;
- } else if (!mWifiEntry.isSaved()) {
- config.SSID = "\"" + mWifiEntry.getTitle() + "\"";
+ } else if (mWifiEntry.isSaved()) {
+ config = new WifiConfiguration(mWifiEntry.getWifiConfiguration());
} else {
- config.networkId = mWifiEntry.getWifiConfiguration().networkId;
- config.hiddenSSID = mWifiEntry.getWifiConfiguration().hiddenSSID;
+ config = new WifiConfiguration();
+ config.SSID = "\"" + mWifiEntry.getTitle() + "\"";
}
config.shared = mSharedCheckBox.isChecked();
switch (mWifiEntrySecurity) {
case WifiEntry.SECURITY_NONE:
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
+ }
break;
case WifiEntry.SECURITY_WEP:
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
+ }
if (mPasswordView.length() != 0) {
int length = mPasswordView.length();
String password = mPasswordView.getText().toString();
@@ -628,7 +632,9 @@
break;
case WifiEntry.SECURITY_PSK:
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
+ }
if (mPasswordView.length() != 0) {
String password = mPasswordView.getText().toString();
if (password.matches("[0-9A-Fa-f]{64}")) {
@@ -642,13 +648,16 @@
case WifiEntry.SECURITY_EAP:
case WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE:
case WifiEntry.SECURITY_EAP_SUITE_B:
- if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) {
- // allowedSuiteBCiphers will be set according to certificate type
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
- } else if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE) {
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
- } else {
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_SUITE_B) {
+ // allowedSuiteBCiphers will be set according to certificate type
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
+ } else if (mWifiEntrySecurity == WifiEntry.SECURITY_EAP_WPA3_ENTERPRISE) {
+ config.setSecurityParams(
+ WifiConfiguration.SECURITY_TYPE_EAP_WPA3_ENTERPRISE);
+ } else {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
+ }
}
config.enterpriseConfig = new WifiEnterpriseConfig();
int eapMethod = mEapMethodSpinner.getSelectedItemPosition();
@@ -790,7 +799,9 @@
}
break;
case WifiEntry.SECURITY_SAE:
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
+ }
if (mPasswordView.length() != 0) {
String password = mPasswordView.getText().toString();
config.preSharedKey = '"' + password + '"';
@@ -798,7 +809,9 @@
break;
case WifiEntry.SECURITY_OWE:
- config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
+ if (mWifiEntry == null || !mWifiEntry.isSaved()) {
+ config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
+ }
break;
default:
diff --git a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
index ea15c43..6612476 100644
--- a/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
+++ b/src/com/android/settings/wifi/factory/WifiFeatureProvider.java
@@ -26,6 +26,7 @@
import androidx.lifecycle.ViewModelStoreOwner;
import com.android.settings.wifi.repository.WifiHotspotRepository;
+import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel;
import com.android.settings.wifi.tether.WifiHotspotSpeedViewModel;
import com.android.settings.wifi.tether.WifiTetherViewModel;
@@ -85,6 +86,17 @@
}
/**
+ * Get WifiHotspotSecurityViewModel
+ */
+ public WifiHotspotSecurityViewModel getWifiHotspotSecurityViewModel(
+ @NotNull ViewModelStoreOwner owner) {
+ WifiHotspotSecurityViewModel viewModel =
+ new ViewModelProvider(owner).get(WifiHotspotSecurityViewModel.class);
+ verboseLog(TAG, "getWifiHotspotSecurityViewModel():" + viewModel);
+ return viewModel;
+ }
+
+ /**
* Get WifiHotspotSpeedViewModel
*/
public WifiHotspotSpeedViewModel getWifiHotspotSpeedViewModel(
diff --git a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
index ff6d883..91de482 100644
--- a/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
+++ b/src/com/android/settings/wifi/repository/WifiHotspotRepository.java
@@ -19,6 +19,8 @@
import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
import static android.net.wifi.SoftApConfiguration.BAND_5GHZ;
import static android.net.wifi.SoftApConfiguration.BAND_6GHZ;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
import static android.net.wifi.WifiAvailableChannel.OP_MODE_SAP;
import android.content.Context;
@@ -81,6 +83,7 @@
protected String mLastPassword;
protected LastPasswordListener mLastPasswordListener = new LastPasswordListener();
+ protected MutableLiveData<Integer> mSecurityType;
protected MutableLiveData<Integer> mSpeedType;
protected Boolean mIsDualBand;
@@ -144,6 +147,7 @@
* Refresh data from the SoftApConfiguration.
*/
public void refresh() {
+ updateSecurityType();
update6gAvailable();
update5gAvailable();
updateSpeedType();
@@ -163,10 +167,70 @@
}
/**
+ * Gets SecurityType LiveData
+ */
+ public LiveData<Integer> getSecurityType() {
+ if (mSecurityType == null) {
+ startAutoRefresh();
+ mSecurityType = new MutableLiveData<>();
+ updateSecurityType();
+ log("getSecurityType():" + mSecurityType.getValue());
+ }
+ return mSecurityType;
+ }
+
+ protected void updateSecurityType() {
+ if (mSecurityType == null) {
+ return;
+ }
+ SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
+ int securityType = (config != null) ? config.getSecurityType() : SECURITY_TYPE_OPEN;
+ log("updateSecurityType(), securityType:" + securityType);
+ mSecurityType.setValue(securityType);
+ }
+
+ /**
+ * Sets SecurityType
+ *
+ * @param securityType the Wi-Fi hotspot security type.
+ */
+ public void setSecurityType(int securityType) {
+ log("setSecurityType():" + securityType);
+ if (mSecurityType == null) {
+ getSecurityType();
+ }
+ if (securityType == mSecurityType.getValue()) {
+ Log.w(TAG, "setSecurityType() is no changed! mSecurityType:"
+ + mSecurityType.getValue());
+ return;
+ }
+ SoftApConfiguration config = mWifiManager.getSoftApConfiguration();
+ if (config == null) {
+ mSecurityType.setValue(SECURITY_TYPE_OPEN);
+ Log.e(TAG, "setSecurityType(), WifiManager#getSoftApConfiguration() return null!");
+ return;
+ }
+ SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder(config);
+ String passphrase = null;
+ if (securityType != SECURITY_TYPE_OPEN) {
+ passphrase = config.getPassphrase();
+ if (TextUtils.isEmpty(passphrase)) {
+ passphrase = generatePassword();
+ }
+ }
+ configBuilder.setPassphrase(passphrase, securityType);
+ setSoftApConfiguration(configBuilder.build());
+
+ mWifiManager.queryLastConfiguredTetheredApPassphraseSinceBoot(
+ mAppContext.getMainExecutor(), mLastPasswordListener);
+ }
+
+ /**
* Gets SpeedType LiveData
*/
public LiveData<Integer> getSpeedType() {
if (mSpeedType == null) {
+ startAutoRefresh();
mSpeedType = new MutableLiveData<>();
updateSpeedType();
log("getSpeedType():" + mSpeedType.getValue());
@@ -230,6 +294,10 @@
if (speedType == SPEED_6GHZ) {
log("setSpeedType(), setBand(BAND_2GHZ_5GHZ_6GHZ)");
configBuilder.setBand(BAND_2GHZ_5GHZ_6GHZ);
+ if (config.getSecurityType() != SECURITY_TYPE_WPA3_SAE) {
+ log("setSpeedType(), setPassphrase(SECURITY_TYPE_WPA3_SAE)");
+ configBuilder.setPassphrase(generatePassword(), SECURITY_TYPE_WPA3_SAE);
+ }
} else if (speedType == SPEED_5GHZ) {
log("setSpeedType(), setBand(BAND_2GHZ_5GHZ)");
configBuilder.setBand(BAND_2GHZ_5GHZ);
diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettings.java b/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettings.java
new file mode 100644
index 0000000..8ff2689
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettings.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import android.app.settings.SettingsEnums;
+import android.os.Bundle;
+
+import androidx.lifecycle.LiveData;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wi-Fi Hotspot Security Settings
+ */
+public class WifiHotspotSecuritySettings extends DashboardFragment implements
+ SelectorWithWidgetPreference.OnClickListener {
+ private static final String TAG = "WifiHotspotSecuritySettings";
+
+ protected WifiHotspotSecurityViewModel mWifiHotspotSecurityViewModel;
+ protected Map<Integer, SelectorWithWidgetPreference> mPreferenceMap = new HashMap<>();
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.WIFI_TETHER_SETTINGS;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.wifi_hotspot_security;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ loadViewModel();
+ }
+
+ protected void loadViewModel() {
+ mWifiHotspotSecurityViewModel = FeatureFactory.getFactory(getContext())
+ .getWifiFeatureProvider().getWifiHotspotSecurityViewModel(this);
+ LiveData<List<WifiHotspotSecurityViewModel.ViewItem>> viewItemListData =
+ mWifiHotspotSecurityViewModel.getViewItemListData();
+ viewItemListData.observe(this, this::onViewItemListDataChanged);
+ // set the onRadioButtonClicked callback to related preference
+ for (WifiHotspotSecurityViewModel.ViewItem viewItem : viewItemListData.getValue()) {
+ SelectorWithWidgetPreference preference = findPreference(viewItem.mKey);
+ preference.setOnClickListener(this);
+ }
+ }
+
+ protected void onViewItemListDataChanged(
+ List<WifiHotspotSecurityViewModel.ViewItem> viewItems) {
+ log("onViewItemListDataChanged(), viewItems:" + viewItems);
+ for (WifiHotspotSecurityViewModel.ViewItem viewItem : viewItems) {
+ SelectorWithWidgetPreference preference = findPreference(viewItem.mKey);
+ if (preference == null) {
+ continue;
+ }
+ if (preference.isChecked() != viewItem.mIsChecked) {
+ preference.setChecked(viewItem.mIsChecked);
+ }
+ if (preference.isEnabled() != viewItem.mIsEnabled) {
+ preference.setEnabled(viewItem.mIsEnabled);
+ if (viewItem.mIsEnabled) {
+ preference.setSummary(null);
+ } else {
+ preference.setSummary(R.string.wifi_hotspot_security_summary_unavailable);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onRadioButtonClicked(SelectorWithWidgetPreference emiter) {
+ String key = emiter.getKey();
+ log("onRadioButtonClicked(), key:" + key);
+ if (key.isEmpty()) {
+ return;
+ }
+ mWifiHotspotSecurityViewModel.handleRadioButtonClicked(key);
+ }
+
+ private void log(String msg) {
+ FeatureFactory.getFactory(getContext()).getWifiFeatureProvider().verboseLog(TAG, msg);
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModel.java b/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModel.java
new file mode 100644
index 0000000..422e40b
--- /dev/null
+++ b/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModel.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION;
+
+import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ;
+
+import android.app.Application;
+
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.wifi.repository.WifiHotspotRepository;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Wi-Fi Hotspot Security View Model for {@link WifiHotspotSecuritySettings}
+ */
+public class WifiHotspotSecurityViewModel extends AndroidViewModel {
+ private static final String TAG = "WifiHotspotSecurityViewModel";
+
+ public static final String KEY_SECURITY_WPA3 = "wifi_hotspot_security_wpa3";
+ public static final String KEY_SECURITY_WPA2_WPA3 = "wifi_hotspot_security_wpa2_wpa3";
+ public static final String KEY_SECURITY_WPA2 = "wifi_hotspot_security_wpa2";
+ public static final String KEY_SECURITY_NONE = "wifi_hotspot_security_none";
+
+ protected Map<Integer, ViewItem> mViewItemMap = new HashMap<>();
+ protected MutableLiveData<List<ViewItem>> mViewInfoListData;
+
+ protected final WifiHotspotRepository mWifiHotspotRepository;
+ protected final Observer<Integer> mSecurityTypeObserver = st -> onSecurityTypeChanged(st);
+ protected final Observer<Integer> mSpeedTypeObserver = st -> onSpeedTypeChanged(st);
+
+ public WifiHotspotSecurityViewModel(
+ @NotNull Application application) {
+ super(application);
+ mViewItemMap.put(SECURITY_TYPE_WPA3_SAE, new ViewItem(KEY_SECURITY_WPA3));
+ mViewItemMap.put(SECURITY_TYPE_WPA3_SAE_TRANSITION, new ViewItem(KEY_SECURITY_WPA2_WPA3));
+ mViewItemMap.put(SECURITY_TYPE_WPA2_PSK, new ViewItem(KEY_SECURITY_WPA2));
+ mViewItemMap.put(SECURITY_TYPE_OPEN, new ViewItem(KEY_SECURITY_NONE));
+
+ mWifiHotspotRepository = FeatureFactory.getFactory(application).getWifiFeatureProvider()
+ .getWifiHotspotRepository();
+ mWifiHotspotRepository.getSecurityType().observeForever(mSecurityTypeObserver);
+ mWifiHotspotRepository.getSpeedType().observeForever(mSpeedTypeObserver);
+ }
+
+ @Override
+ protected void onCleared() {
+ mWifiHotspotRepository.getSecurityType().removeObserver(mSecurityTypeObserver);
+ mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver);
+ }
+
+ protected void onSecurityTypeChanged(int securityType) {
+ log("onSecurityTypeChanged(), securityType:" + securityType);
+ for (Map.Entry<Integer, ViewItem> entry : mViewItemMap.entrySet()) {
+ entry.getValue().mIsChecked = entry.getKey().equals(securityType);
+ }
+ updateViewItemListData();
+ }
+
+ protected void onSpeedTypeChanged(Integer speedType) {
+ log("onSpeedTypeChanged(), speedType:" + speedType);
+ boolean isWpa3Only = (speedType == SPEED_6GHZ);
+ for (Map.Entry<Integer, ViewItem> entry : mViewItemMap.entrySet()) {
+ if (entry.getKey() != SECURITY_TYPE_WPA3_SAE) {
+ entry.getValue().mIsEnabled = !isWpa3Only;
+ }
+ }
+ updateViewItemListData();
+ }
+
+ /**
+ * Handle RadioButton Clicked
+ */
+ public void handleRadioButtonClicked(String key) {
+ log("handleRadioButtonClicked(), key:" + key);
+ for (Map.Entry<Integer, ViewItem> entry : mViewItemMap.entrySet()) {
+ ViewItem viewItem = entry.getValue();
+ if (viewItem.mKey.equals(key)) {
+ mWifiHotspotRepository.setSecurityType(entry.getKey());
+ return;
+ }
+ }
+ }
+
+ /**
+ * Gets ViewItemList LiveData
+ */
+ public LiveData<List<ViewItem>> getViewItemListData() {
+ if (mViewInfoListData == null) {
+ mViewInfoListData = new MutableLiveData<>();
+ updateViewItemListData();
+ log("getViewItemListData(), mViewInfoListData:" + mViewInfoListData.getValue());
+ }
+ return mViewInfoListData;
+ }
+
+ protected void updateViewItemListData() {
+ if (mViewInfoListData == null) {
+ return;
+ }
+ mViewInfoListData.setValue(mViewItemMap.values().stream().toList());
+ }
+
+ /**
+ * Wi-Fi Hotspot View Item
+ */
+ public static final class ViewItem {
+ String mKey;
+ boolean mIsChecked;
+ boolean mIsEnabled = true;
+
+ public ViewItem(String key) {
+ mKey = key;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder("ViewItem:{")
+ .append("Key:").append(mKey)
+ .append(",IsChecked:").append(mIsChecked)
+ .append(",IsEnabled:").append(mIsEnabled)
+ .append('}').toString();
+ }
+ }
+
+ private void log(String msg) {
+ FeatureFactory.getFactory(getApplication()).getWifiFeatureProvider().verboseLog(TAG, msg);
+ }
+}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index 3a3691a..bdb1a2e 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -72,6 +72,8 @@
static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY =
WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY;
@VisibleForTesting
+ static final String KEY_WIFI_HOTSPOT_SECURITY = "wifi_hotspot_security";
+ @VisibleForTesting
static final String KEY_WIFI_HOTSPOT_SPEED = "wifi_hotspot_speed";
private WifiTetherSwitchBarController mSwitchBarController;
@@ -91,6 +93,8 @@
@VisibleForTesting
WifiTetherViewModel mWifiTetherViewModel;
@VisibleForTesting
+ Preference mWifiHotspotSecurity;
+ @VisibleForTesting
Preference mWifiHotspotSpeed;
static {
@@ -132,6 +136,10 @@
mWifiTetherViewModel = FeatureFactory.getFactory(getContext()).getWifiFeatureProvider()
.getWifiTetherViewModel(this);
+ mWifiHotspotSecurity = findPreference(KEY_WIFI_HOTSPOT_SECURITY);
+ if (mWifiHotspotSecurity != null && mWifiHotspotSecurity.isVisible()) {
+ mWifiTetherViewModel.getSecuritySummary().observe(this, this::onSecuritySummaryChanged);
+ }
mWifiHotspotSpeed = findPreference(KEY_WIFI_HOTSPOT_SPEED);
if (mWifiHotspotSpeed != null && mWifiHotspotSpeed.isVisible()) {
mWifiTetherViewModel.getSpeedSummary().observe(this, this::onSpeedSummaryChanged);
@@ -206,6 +214,10 @@
}
}
+ protected void onSecuritySummaryChanged(Integer securityResId) {
+ mWifiHotspotSecurity.setSummary(securityResId);
+ }
+
protected void onSpeedSummaryChanged(Integer summaryResId) {
mWifiHotspotSpeed.setSummary(summaryResId);
}
diff --git a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java
index 8cb74c3..6bb2cd5 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherViewModel.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherViewModel.java
@@ -16,11 +16,15 @@
package com.android.settings.wifi.tether;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION;
+
import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ;
import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ;
import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ;
import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ;
-import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_UNKNOWN;
import android.app.Application;
import android.net.wifi.SoftApConfiguration;
@@ -28,7 +32,7 @@
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.Transformations;
+import androidx.lifecycle.Observer;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
@@ -45,9 +49,19 @@
public class WifiTetherViewModel extends AndroidViewModel {
private static final String TAG = "WifiTetherViewModel";
- protected static Map<Integer, Integer> sSpeedSummaryResMap = new HashMap<>();
+ static Map<Integer, Integer> sSecuritySummaryResMap = new HashMap<>();
+
static {
- sSpeedSummaryResMap.put(SPEED_UNKNOWN, R.string.summary_placeholder);
+ sSecuritySummaryResMap.put(SECURITY_TYPE_WPA3_SAE, R.string.wifi_security_sae);
+ sSecuritySummaryResMap.put(SECURITY_TYPE_WPA3_SAE_TRANSITION,
+ R.string.wifi_security_psk_sae);
+ sSecuritySummaryResMap.put(SECURITY_TYPE_WPA2_PSK, R.string.wifi_security_wpa2);
+ sSecuritySummaryResMap.put(SECURITY_TYPE_OPEN, R.string.wifi_security_none);
+ }
+
+ static Map<Integer, Integer> sSpeedSummaryResMap = new HashMap<>();
+
+ static {
sSpeedSummaryResMap.put(SPEED_2GHZ, R.string.wifi_hotspot_speed_summary_2g);
sSpeedSummaryResMap.put(SPEED_5GHZ, R.string.wifi_hotspot_speed_summary_5g);
sSpeedSummaryResMap.put(SPEED_6GHZ, R.string.wifi_hotspot_speed_summary_6g);
@@ -55,18 +69,22 @@
}
protected final WifiHotspotRepository mWifiHotspotRepository;
+ protected MutableLiveData<Integer> mSecuritySummary;
protected MutableLiveData<Integer> mSpeedSummary;
+ protected final Observer<Integer> mSecurityTypeObserver = st -> onSecurityTypeChanged(st);
+ protected final Observer<Integer> mSpeedTypeObserver = st -> onSpeedTypeChanged(st);
+
public WifiTetherViewModel(@NotNull Application application) {
super(application);
mWifiHotspotRepository = FeatureFactory.getFactory(application).getWifiFeatureProvider()
.getWifiHotspotRepository();
- mWifiHotspotRepository.setAutoRefresh(true);
}
@Override
protected void onCleared() {
- mWifiHotspotRepository.setAutoRefresh(false);
+ mWifiHotspotRepository.getSecurityType().removeObserver(mSecurityTypeObserver);
+ mWifiHotspotRepository.getSpeedType().removeObserver(mSpeedTypeObserver);
}
/**
@@ -86,17 +104,40 @@
}
/**
+ * Gets SecuritySummary LiveData
+ */
+ public LiveData<Integer> getSecuritySummary() {
+ if (mSecuritySummary == null) {
+ mSecuritySummary = new MutableLiveData<>();
+ mWifiHotspotRepository.getSecurityType().observeForever(mSecurityTypeObserver);
+ }
+ return mSecuritySummary;
+ }
+
+ protected void onSecurityTypeChanged(int securityType) {
+ int resId = R.string.summary_placeholder;
+ if (sSecuritySummaryResMap.containsKey(securityType)) {
+ resId = sSecuritySummaryResMap.get(securityType);
+ }
+ mSecuritySummary.setValue(resId);
+ }
+
+ /**
* Gets SpeedSummary LiveData
*/
public LiveData<Integer> getSpeedSummary() {
if (mSpeedSummary == null) {
mSpeedSummary = new MutableLiveData<>();
- mWifiHotspotRepository.getSpeedType().observeForever(this::onSpeedTypeChanged);
+ mWifiHotspotRepository.getSpeedType().observeForever(mSpeedTypeObserver);
}
- return Transformations.distinctUntilChanged(mSpeedSummary);
+ return mSpeedSummary;
}
protected void onSpeedTypeChanged(Integer speedType) {
- mSpeedSummary.setValue(sSpeedSummaryResMap.get(speedType));
+ int resId = R.string.summary_placeholder;
+ if (sSpeedSummaryResMap.containsKey(speedType)) {
+ resId = sSpeedSummaryResMap.get(speedType);
+ }
+ mSpeedSummary.setValue(resId);
}
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/RequestPermissionHelperTest.kt b/tests/robotests/src/com/android/settings/bluetooth/RequestPermissionHelperTest.kt
new file mode 100644
index 0000000..ce0792c
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/RequestPermissionHelperTest.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2023 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.bluetooth
+
+import androidx.activity.ComponentActivity
+import com.android.settings.R
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.spy
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.android.controller.ActivityController
+import org.robolectric.annotation.Config
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(RobolectricTestRunner::class)
+@Config(shadows = [ShadowAlertDialogCompat::class])
+class RequestPermissionHelperTest {
+ private lateinit var activityController: ActivityController<ComponentActivity>
+
+ @Before
+ fun setUp() {
+ activityController =
+ ActivityController.of(ComponentActivity()).create().start().postCreate(null).resume()
+ }
+
+ @After
+ fun tearDown() {
+ activityController.pause().stop().destroy()
+ }
+
+ @Test
+ fun requestEnable_withAppLabelAndNoTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = "App Label",
+ timeout = -1,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs("App Label wants to turn on Bluetooth")
+ }
+
+ @Test
+ fun requestEnable_withAppLabelAndZeroTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = "App Label",
+ timeout = 0,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs(
+ "App Label wants to turn on Bluetooth and make your phone visible to other devices. " +
+ "You can change this later in Bluetooth settings."
+ )
+ }
+
+ @Test
+ fun requestEnable_withAppLabelAndNormalTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = "App Label",
+ timeout = 120,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs(
+ "App Label wants to turn on Bluetooth and make your phone visible to other devices " +
+ "for 120 seconds."
+ )
+ }
+
+ @Test
+ fun requestEnable_withNoAppLabelAndNoTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = null,
+ timeout = -1,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs("An app wants to turn on Bluetooth")
+ }
+
+ @Test
+ fun requestEnable_withNoAppLabelAndZeroTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = null,
+ timeout = 0,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs(
+ "An app wants to turn on Bluetooth and make your phone visible to other devices. " +
+ "You can change this later in Bluetooth settings."
+ )
+ }
+
+ @Test
+ fun requestEnable_withNoAppLabelAndNormalTimeout_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = null,
+ timeout = 120,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs(
+ "An app wants to turn on Bluetooth and make your phone visible to other devices for " +
+ "120 seconds."
+ )
+ }
+
+ @Test
+ fun requestEnable_whenAutoConfirm_onAllowIsCalled() {
+ val activity = getActivityWith(autoConfirm = true)
+ var onAllowCalled = false
+
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = null,
+ timeout = -1,
+ onAllow = { onAllowCalled = true },
+ onDeny = {},
+ )
+
+ assertThat(onAllowCalled).isTrue()
+ }
+
+ @Test
+ fun requestEnable_whenNotAutoConfirm_onAllowIsNotCalledWhenRequest() {
+ val activity = getActivityWith(autoConfirm = false)
+ var onAllowCalled = false
+
+ RequestPermissionHelper.requestEnable(
+ context = activity,
+ appLabel = null,
+ timeout = -1,
+ onAllow = { onAllowCalled = true },
+ onDeny = {},
+ )
+
+ assertThat(onAllowCalled).isFalse()
+ }
+
+ @Test
+ fun requestDisable_withAppLabel_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestDisable(
+ context = activity,
+ appLabel = "App Label",
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs("App Label wants to turn off Bluetooth")
+ }
+
+ @Test
+ fun requestDisable_withNoAppLabel_hasCorrectMessage() {
+ val activity = activityController.get()
+ RequestPermissionHelper.requestDisable(
+ context = activity,
+ appLabel = null,
+ onAllow = {},
+ onDeny = {},
+ )
+
+ assertLatestMessageIs("An app wants to turn off Bluetooth")
+ }
+
+ @Test
+ fun requestDisable_whenAutoConfirm_onAllowIsCalled() {
+ val activity = getActivityWith(autoConfirm = true)
+ var onAllowCalled = false
+
+ RequestPermissionHelper.requestDisable(
+ context = activity,
+ appLabel = null,
+ onAllow = { onAllowCalled = true },
+ onDeny = {},
+ )
+
+ assertThat(onAllowCalled).isTrue()
+ }
+
+ @Test
+ fun requestDisable_whenNotAutoConfirm_onAllowIsNotCalledWhenRequest() {
+ val activity = getActivityWith(autoConfirm = false)
+ var onAllowCalled = false
+
+ RequestPermissionHelper.requestDisable(
+ context = activity,
+ appLabel = null,
+ onAllow = { onAllowCalled = true },
+ onDeny = {},
+ )
+
+ assertThat(onAllowCalled).isFalse()
+ }
+
+ private fun getActivityWith(autoConfirm: Boolean): ComponentActivity {
+ val activity = spy(activityController.get())
+ val spyResources = spy(activity.resources)
+ whenever(activity.resources).thenReturn(spyResources)
+ whenever(spyResources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog))
+ .thenReturn(autoConfirm)
+ return activity
+ }
+
+ private fun assertLatestMessageIs(message: String) {
+ val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
+ val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
+ assertThat(shadowDialog.message.toString()).isEqualTo(message)
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/ForceEnableNotesRolePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/ForceEnableNotesRolePreferenceControllerTest.java
new file mode 100644
index 0000000..32a28fe
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/development/ForceEnableNotesRolePreferenceControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.development;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+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 ForceEnableNotesRolePreferenceControllerTest {
+
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private SwitchPreference mPreference;
+
+ private ForceEnableNotesRolePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = new ForceEnableNotesRolePreferenceController(
+ RuntimeEnvironment.application) {
+ private boolean mEnabled;
+
+ protected boolean isEnabled() {
+ return mEnabled;
+ }
+
+ protected void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ }
+ };
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ mController.displayPreference(mScreen);
+ }
+
+ @Test
+ public void updateState_enabled_preferenceShouldBeChecked() {
+ mController.setEnabled(true);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(true);
+ }
+
+ @Test
+ public void updateState_disabled_preferenceShouldBeUnchecked() {
+ mController.setEnabled(false);
+ mController.updateState(mPreference);
+
+ verify(mPreference).setChecked(false);
+ }
+
+ @Test
+ public void onPreferenceChange_checked_shouldBeEnabled() {
+ mController.onPreferenceChange(mPreference, true);
+
+ assertTrue(mController.isEnabled());
+ }
+
+ @Test
+ public void onPreferenceChange_unchecked_shouldNotBeEnabled() {
+ mController.onPreferenceChange(mPreference, false);
+
+ assertFalse(mController.isEnabled());
+ }
+
+ @Test
+ public void onDeveloperOptionsSwitchDisabled_preferenceShouldNotBeEnabled() {
+ mController.onDeveloperOptionsSwitchDisabled();
+
+ assertFalse(mController.isEnabled());
+ verify(mPreference).setEnabled(false);
+ verify(mPreference).setChecked(false);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
index 405ea12..3ad14e5 100644
--- a/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceControllerTest.java
@@ -74,7 +74,7 @@
mController.setConfig(mConfig);
mApplicationInfo.uid = 1;
- when(mContext.getString(R.string.config_defaultWellbeingPackage)).thenReturn(TEST_PACKAGE);
+ when(mContext.getString(R.string.config_systemWellbeing)).thenReturn(TEST_PACKAGE);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
doReturn(mApplicationInfo).when(mPackageManager).getApplicationInfo(
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java
index 7f50568..85d6fab 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomBedtimePreferenceControllerTest.java
@@ -77,7 +77,7 @@
when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getString(com.android.internal.R.string.config_defaultWellbeingPackage))
+ when(mResources.getString(com.android.internal.R.string.config_systemWellbeing))
.thenReturn("wellbeing");
when(mScreen.findPreference(anyString())).thenReturn(mFooterPreference);
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
index d776e5e..d95635e 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
@@ -84,7 +84,7 @@
when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager);
when(mContext.getResources()).thenReturn(mResources);
when(mResources.getConfiguration()).thenReturn(mConfigNightNo);
- when(mResources.getString(com.android.internal.R.string.config_defaultWellbeingPackage))
+ when(mResources.getString(com.android.internal.R.string.config_systemWellbeing))
.thenReturn("wellbeing");
when(mContext.getString(R.string.dark_ui_auto_mode_never)).thenReturn("never");
diff --git a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
index 6e687bb..e173add 100644
--- a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
@@ -72,7 +72,7 @@
mApplicationInfo.uid = 1;
when(mContext.getString(
- com.android.internal.R.string.config_defaultWellbeingPackage)).thenReturn(
+ com.android.internal.R.string.config_systemWellbeing)).thenReturn(
TEST_PACKAGE);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
index 1dfe6e2..9ababe7 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/detectors/IncompatibleChargerDetectorTest.java
@@ -25,7 +25,6 @@
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
-import com.android.settings.fuelgauge.BatteryInfo;
import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
import org.junit.Before;
@@ -42,7 +41,6 @@
@RunWith(RobolectricTestRunner.class)
public final class IncompatibleChargerDetectorTest {
- @Mock private BatteryInfo mBatteryInfo;
@Mock private UsbPort mUsbPort;
@Mock private UsbManager mUsbManager;
@Mock private UsbPortStatus mUsbPortStatus;
@@ -55,14 +53,11 @@
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
- mIncompatibleChargerDetector =
- new IncompatibleChargerDetector(mContext, mBatteryInfo);
+ mIncompatibleChargerDetector = new IncompatibleChargerDetector(mContext);
}
@Test
- public void detect_unplugDevice_shouldNotShowTip() {
- mBatteryInfo.pluggedStatus = 0;
-
+ public void detect_withoutIncompatibleCharger_shouldNotShowTip() {
BatteryTip batteryTip = mIncompatibleChargerDetector.detect();
assertThat(batteryTip.isVisible()).isFalse();
@@ -70,18 +65,7 @@
}
@Test
- public void detect_plugDeviceWithoutIncompatibleCharger_shouldNotShowTip() {
- mBatteryInfo.pluggedStatus = 1;
-
- BatteryTip batteryTip = mIncompatibleChargerDetector.detect();
-
- assertThat(batteryTip.isVisible()).isFalse();
- assertThat(batteryTip.getState()).isEqualTo(BatteryTip.StateType.INVISIBLE);
- }
-
- @Test
- public void detect_plugDeviceWithIncompatibleCharger_showTip() {
- mBatteryInfo.pluggedStatus = 1;
+ public void detect_withIncompatibleCharger_showTip() {
setupIncompatibleCharging();
BatteryTip batteryTip = mIncompatibleChargerDetector.detect();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
index f18228b..9cebd19 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -150,11 +150,12 @@
// Ignore fast refresh ui from the data processor callback.
verify(mHourlyChartView, atLeast(0)).setViewModel(null);
verify(mHourlyChartView, atLeastOnce()).setViewModel(new BatteryChartViewModel(
- List.of(100, 97, 95, 66),
- List.of(1619251200000L /* 8 AM */,
+ List.of(100, 99, 97, 95, 66),
+ List.of(1619247660000L /* 7:01 AM */,
+ 1619251200000L /* 8 AM */,
1619258400000L /* 10 AM */,
1619265600000L /* 12 PM */,
- 1619272800000L /* 2 PM */),
+ 1619265720000L /* now (12:02 PM) */),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator));
}
@@ -168,10 +169,10 @@
BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
List.of(100, 83, 59, 66),
// "Sat", "Sun", "Mon", "Mon"
- List.of(1619251200000L /* Sat */,
+ List.of(1619247660000L /* Sat */,
1619308800000L /* Sun */,
1619395200000L /* Mon */,
- 1619467200000L /* Mon */),
+ 1619460120000L /* Mon */),
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS,
mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
@@ -194,8 +195,9 @@
expectedDailyViewModel.setSelectedIndex(0);
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
- List.of(100, 97, 95, 93, 91, 89, 87, 85, 83),
- List.of(1619251200000L /* 8 AM */,
+ List.of(100, 99, 97, 95, 93, 91, 89, 87, 85, 83),
+ List.of(1619247660000L /* 7:01 AM */,
+ 1619251200000L /* 8 AM */,
1619258400000L /* 10 AM */,
1619265600000L /* 12 PM */,
1619272800000L /* 2 PM */,
@@ -262,7 +264,7 @@
1619445600000L /* 2 PM */,
1619452800000L /* 4 PM */,
1619460000000L /* 6 PM */,
- 1619467200000L /* 8 PM */),
+ 1619460120000L /* now (6:02 PM) */),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS,
mBatteryChartPreferenceController.mHourlyChartLabelTextGenerator));
@@ -327,7 +329,7 @@
public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
- mBatteryChartPreferenceController.mHourlyChartIndex = 1;
+ mBatteryChartPreferenceController.mHourlyChartIndex = 2;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
"10 AM - 12 PM");
@@ -344,6 +346,36 @@
}
@Test
+ public void selectedSlotText_selectFirstSlot_withMinuteText() {
+ mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
+ mBatteryChartPreferenceController.mDailyChartIndex = 0;
+ mBatteryChartPreferenceController.mHourlyChartIndex = 0;
+
+ assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
+ "7:01 AM - 8 AM");
+ }
+
+ @Test
+ public void selectedSlotText_selectLastSlot_withNowText() {
+ mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
+ mBatteryChartPreferenceController.mDailyChartIndex = 0;
+ mBatteryChartPreferenceController.mHourlyChartIndex = 3;
+
+ assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
+ "12 PM - now");
+ }
+
+ @Test
+ public void selectedSlotText_selectOnlySlot_withMinuteAndNowText() {
+ mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(1));
+ mBatteryChartPreferenceController.mDailyChartIndex = 0;
+ mBatteryChartPreferenceController.mHourlyChartIndex = 0;
+
+ assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
+ "7:01 AM - now");
+ }
+
+ @Test
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
final int expectedDailyIndex = 1;
final int expectedHourlyIndex = 2;
@@ -373,7 +405,7 @@
final int totalHour = BatteryChartPreferenceController.getTotalHours(batteryLevelData);
// Only calculate the even hours.
- assertThat(totalHour).isEqualTo(60);
+ assertThat(totalHour).isEqualTo(59);
}
private static Long generateTimestamp(int index) {
@@ -403,10 +435,14 @@
final BatteryHistEntry entry = new BatteryHistEntry(values);
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
entryMap.put("fake_entry_key" + index, entry);
- batteryHistoryMap.put(generateTimestamp(index), entryMap);
+ long timestamp = generateTimestamp(index);
+ if (index == 0) {
+ timestamp += DateUtils.MINUTE_IN_MILLIS;
+ }
+ batteryHistoryMap.put(timestamp, entryMap);
}
DataProcessor.sTestCurrentTimeMillis =
- generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS;
+ generateTimestamp(numOfHours - 1) + DateUtils.MINUTE_IN_MILLIS * 2;
return batteryHistoryMap;
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index 16fec09..8d0a839 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -176,7 +176,7 @@
}
@Test
- public void removeAndCacheAllUnusedPreferences_removePerf_buildCacheAndRemoveAllPreference() {
+ public void removeAndCacheAllUnusedPreferences_removePref_buildCacheAndRemoveAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
doReturn(PREF_KEY2).when(mBatteryHistEntry).getKey();
@@ -193,7 +193,7 @@
}
@Test
- public void removeAndCacheAllUnusedPreferences_keepPerf_KeepAllPreference() {
+ public void removeAndCacheAllUnusedPreferences_keepPref_KeepAllPreference() {
doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
index 753a7f7..b610cfb 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -102,7 +102,7 @@
AppUsageEventEntity.KEY_UID,
AppUsageEventEntity.KEY_PACKAGE_NAME,
AppUsageEventEntity.KEY_TIMESTAMP});
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
doReturn(true).when(mUserManager).isUserUnlocked(anyInt());
mDataProcessManager.start();
@@ -171,7 +171,7 @@
cursor.addRow(new Object[] {
AppUsageEventType.ACTIVITY_STOPPED.getNumber(), /*timestamp=*/ 6, /*userId=*/ 1,
/*instanceId=*/ 2, packageName});
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
final DataProcessManager dataProcessManager = new DataProcessManager(
mContext, /*handler=*/ null, /*rawStartTimestamp=*/ 2L, /*callbackFunction=*/ null,
@@ -240,7 +240,7 @@
AppUsageEventEntity.KEY_TIMESTAMP});
// Adds fake data into the cursor.
cursor.addRow(new Object[] {101L, "app name1", 1001L});
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
mDataProcessManager.start();
@@ -263,7 +263,7 @@
}
@Test
- public void getBatteryLevelData_notEnoughData_returnNull() {
+ public void getBatteryLevelData_allDataInOneHour_returnExpectedResult() {
// The timestamps and the current time are within half hour before an even hour.
final long[] timestamps = {
DateUtils.HOUR_IN_MILLIS * 2 - 300L,
@@ -274,9 +274,26 @@
createHistoryMap(timestamps, levels);
DataProcessor.sTestCurrentTimeMillis = timestamps[timestamps.length - 1];
- assertThat(DataProcessManager.getBatteryLevelData(
- mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
- .isNull();
+ final BatteryLevelData resultData =
+ DataProcessManager.getBatteryLevelData(
+ mContext,
+ /*handler=*/ null,
+ batteryHistoryMap,
+ /*asyncResponseDelegate=*/ null);
+
+
+ final List<Long> expectedDailyTimestamps = List.of(
+ DateUtils.HOUR_IN_MILLIS * 2 - 300L,
+ DateUtils.HOUR_IN_MILLIS * 2 - 100L);
+ final List<Integer> expectedDailyLevels = List.of(100, 66);
+ final List<List<Long>> expectedHourlyTimestamps = List.of(expectedDailyTimestamps);
+ final List<List<Integer>> expectedHourlyLevels = List.of(expectedDailyLevels);
+ verifyExpectedBatteryLevelData(
+ resultData,
+ expectedDailyTimestamps,
+ expectedDailyLevels,
+ expectedHourlyTimestamps,
+ expectedHourlyLevels);
}
@Test
@@ -297,7 +314,7 @@
final List<Long> expectedDailyTimestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
- 1640973600000L); // 2022-01-01 02:00:00
+ 1640970000000L); // 2022-01-01 01:00:00
final List<Integer> expectedDailyLevels = List.of(100, 66);
final List<List<Long>> expectedHourlyTimestamps = List.of(expectedDailyTimestamps);
final List<List<Integer>> expectedHourlyLevels = List.of(expectedDailyLevels);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
index 66e48c0..ea2db86 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessorTest.java
@@ -42,7 +42,6 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.UserManager;
-import android.text.format.DateUtils;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
@@ -409,24 +408,21 @@
// Timezone GMT+8
final long[] expectedTimestamps = {
- 1640966400000L, // 2022-01-01 00:00:00
+ 1640966700000L, // 2022-01-01 00:05:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L, // 2022-01-01 04:00:00
- 1640984400000L, // 2022-01-01 05:00:00
- 1640988000000L // 2022-01-01 06:00:00
+ 1640981400000L // 2022-01-01 04:10:00
};
- final int[] expectedLevels = {100, 94, 90, 84, 56, 98, 98};
+ final int[] expectedLevels = {100, 94, 90, 84, 56, 98};
assertThat(resultMap).hasSize(expectedLevels.length);
- for (int index = 0; index < 5; index++) {
+ for (int index = 0; index < expectedLevels.length - 1; index++) {
assertThat(resultMap.get(expectedTimestamps[index]).get(FAKE_ENTRY_KEY).mBatteryLevel)
.isEqualTo(expectedLevels[index]);
}
- for (int index = 5; index < 7; index++) {
- assertThat(resultMap.get(expectedTimestamps[index]).containsKey(
- DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)).isTrue();
- }
+ assertThat(resultMap.get(expectedTimestamps[expectedLevels.length - 1]).containsKey(
+ DataProcessor.CURRENT_TIME_BATTERY_HISTORY_PLACEHOLDER)).isTrue();
}
@Test
@@ -589,7 +585,8 @@
1667782800000L, // 2022-11-06 17:00:00
1667790000000L, // 2022-11-06 19:00:00
1667797200000L, // 2022-11-06 21:00:00
- 1667804400000L // 2022-11-06 23:00:00
+ 1667804400000L, // 2022-11-06 23:00:00
+ 1667808000000L // 2022-11-07 00:00:00
),
List.of(
1667808000000L, // 2022-11-07 00:00:00
@@ -621,6 +618,7 @@
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
+ expectedHourlyLevels2.add(null);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
@@ -683,7 +681,8 @@
1647216000000L, // 2022-03-13 17:00:00
1647223200000L, // 2022-03-13 19:00:00
1647230400000L, // 2022-03-13 21:00:00
- 1647237600000L // 2022-03-13 23:00:00
+ 1647237600000L, // 2022-03-13 23:00:00
+ 1647241200000L // 2022-03-14 00:00:00
),
List.of(
1647241200000L, // 2022-03-14 00:00:00
@@ -708,6 +707,7 @@
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(null);
+ expectedHourlyLevels2.add(null);
final List<Integer> expectedHourlyLevels3 = new ArrayList<>();
expectedHourlyLevels3.add(null);
expectedHourlyLevels3.add(null);
@@ -736,49 +736,95 @@
@Test
public void getTimestampSlots_startWithEvenHour_returnExpectedResult() {
final Calendar startCalendar = Calendar.getInstance();
+ startCalendar.clear();
startCalendar.set(2022, 6, 5, 6, 30, 50); // 2022-07-05 06:30:50
+ final long startTimestamp = startCalendar.getTimeInMillis();
final Calendar endCalendar = Calendar.getInstance();
+ endCalendar.clear();
endCalendar.set(2022, 6, 5, 22, 30, 50); // 2022-07-05 22:30:50
+ final long endTimestamp = endCalendar.getTimeInMillis();
- final Calendar expectedStartCalendar = Calendar.getInstance();
- expectedStartCalendar.set(2022, 6, 5, 6, 0, 0); // 2022-07-05 06:00:00
- final Calendar expectedEndCalendar = Calendar.getInstance();
- expectedEndCalendar.set(2022, 6, 6, 0, 0, 0); // 2022-07-05 22:00:00
- verifyExpectedTimestampSlots(
- startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar);
+ final Calendar calendar = Calendar.getInstance();
+ List<Long> expectedTimestamps = new ArrayList<>();
+ calendar.clear();
+ calendar.set(2022, 6, 5, 6, 30, 50); // 2022-07-05 06:30:50
+ expectedTimestamps.add(calendar.getTimeInMillis());
+ for (int hour = 7; hour <= 22; hour++) {
+ calendar.clear();
+ calendar.set(2022, 6, 5, hour, 0, 0); // 2022-07-05 <hour>:00:00
+ expectedTimestamps.add(calendar.getTimeInMillis());
+ }
+ calendar.clear();
+ calendar.set(2022, 6, 5, 22, 30, 50); // 2022-07-05 22:30:50
+ expectedTimestamps.add(calendar.getTimeInMillis());
+
+ verifyExpectedTimestampSlots(startTimestamp, endTimestamp, expectedTimestamps);
}
@Test
public void getTimestampSlots_startWithOddHour_returnExpectedResult() {
final Calendar startCalendar = Calendar.getInstance();
+ startCalendar.clear();
startCalendar.set(2022, 6, 5, 5, 0, 50); // 2022-07-05 05:00:50
+ final long startTimestamp = startCalendar.getTimeInMillis();
final Calendar endCalendar = Calendar.getInstance();
- endCalendar.set(2022, 6, 6, 21, 0, 50); // 2022-07-06 21:00:50
+ endCalendar.clear();
+ endCalendar.set(2022, 6, 5, 21, 0, 50); // 2022-07-05 21:00:50
+ final long endTimestamp = endCalendar.getTimeInMillis();
- final Calendar expectedStartCalendar = Calendar.getInstance();
- expectedStartCalendar.set(2022, 6, 5, 6, 0, 0); // 2022-07-05 06:00:00
- final Calendar expectedEndCalendar = Calendar.getInstance();
- expectedEndCalendar.set(2022, 6, 6, 22, 0, 0); // 2022-07-06 20:00:00
- verifyExpectedTimestampSlots(
- startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar);
+ final Calendar calendar = Calendar.getInstance();
+ List<Long> expectedTimestamps = new ArrayList<>();
+ calendar.clear();
+ calendar.set(2022, 6, 5, 5, 0, 50); // 2022-07-05 05:00:50
+ expectedTimestamps.add(calendar.getTimeInMillis());
+ for (int hour = 6; hour <= 21; hour++) {
+ calendar.clear();
+ calendar.set(2022, 6, 5, hour, 0, 0); // 2022-07-05 <hour>:00:00
+ expectedTimestamps.add(calendar.getTimeInMillis());
+ }
+ calendar.clear();
+ calendar.set(2022, 6, 5, 21, 0, 50); // 2022-07-05 21:00:50
+ expectedTimestamps.add(calendar.getTimeInMillis());
+
+ verifyExpectedTimestampSlots(startTimestamp, endTimestamp, expectedTimestamps);
}
@Test
public void getDailyTimestamps_notEnoughData_returnEmptyList() {
assertThat(DataProcessor.getDailyTimestamps(new ArrayList<>())).isEmpty();
assertThat(DataProcessor.getDailyTimestamps(List.of(100L))).isEmpty();
- assertThat(DataProcessor.getDailyTimestamps(List.of(100L, 5400000L))).isEmpty();
}
@Test
- public void getDailyTimestamps_OneHourDataPerDay_returnEmptyList() {
+ public void getDailyTimestamps_allDataInOneHour_returnExpectedList() {
+ // Timezone GMT+8
+ final List<Long> timestamps = List.of(
+ 1640970006000L, // 2022-01-01 01:00:06
+ 1640973608000L // 2022-01-01 01:00:08
+ );
+
+ final List<Long> expectedTimestamps = List.of(
+ 1640970006000L, // 2022-01-01 01:00:06
+ 1640973608000L // 2022-01-01 01:00:08
+ );
+ assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
+ }
+
+ @Test
+ public void getDailyTimestamps_OneHourDataPerDay_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L // 2022-01-02 01:00:00
);
- assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEmpty();
+
+ final List<Long> expectedTimestamps = List.of(
+ 1641049200000L, // 2022-01-01 23:00:00
+ 1641052800000L, // 2022-01-02 00:00:00
+ 1641056400000L // 2022-01-02 01:00:00
+ );
+ assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
@@ -830,6 +876,7 @@
);
final List<Long> expectedTimestamps = List.of(
+ 1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
@@ -871,7 +918,8 @@
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
- 1641225600000L // 2022-01-04 00:00:00
+ 1641225600000L, // 2022-01-04 00:00:00
+ 1641229200000L // 2022-01-04 01:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@@ -923,27 +971,6 @@
}
@Test
- public void getTimestampOfNextDay_returnExpectedResult() {
- // 2021-02-28 06:00:00 => 2021-03-01 00:00:00
- assertThat(DataProcessor.getTimestampOfNextDay(1614463200000L))
- .isEqualTo(1614528000000L);
- // 2021-12-31 16:00:00 => 2022-01-01 00:00:00
- assertThat(DataProcessor.getTimestampOfNextDay(1640937600000L))
- .isEqualTo(1640966400000L);
- }
-
- @Test
- public void isForDailyChart_returnExpectedResult() {
- assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ true, 0L)).isTrue();
- // 2022-01-01 00:00:00
- assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ false, 1640966400000L))
- .isTrue();
- // 2022-01-01 01:00:05
- assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ false, 1640970005000L))
- .isFalse();
- }
-
- @Test
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
@@ -2046,24 +2073,16 @@
}
private static void verifyExpectedTimestampSlots(
- final Calendar start,
- final Calendar current,
- final Calendar expectedStart,
- final Calendar expectedEnd) {
- expectedStart.set(Calendar.MILLISECOND, 0);
- expectedEnd.set(Calendar.MILLISECOND, 0);
+ final long startTimestamp,
+ final long currentTimestamp,
+ final List<Long> expectedTimestamps) {
final ArrayList<Long> timestampSlots = new ArrayList<>();
- timestampSlots.add(start.getTimeInMillis());
- final List<Long> resultList =
- DataProcessor.getTimestampSlots(timestampSlots, current.getTimeInMillis());
+ timestampSlots.add(startTimestamp);
- for (int index = 0; index < resultList.size(); index++) {
- final long expectedTimestamp =
- expectedStart.getTimeInMillis() + index * DateUtils.HOUR_IN_MILLIS;
- assertThat(resultList.get(index)).isEqualTo(expectedTimestamp);
- }
- assertThat(resultList.get(resultList.size() - 1))
- .isEqualTo(expectedEnd.getTimeInMillis());
+ final List<Long> resultList =
+ DataProcessor.getTimestampSlots(timestampSlots, currentTimestamp);
+
+ assertThat(resultList).isEqualTo(expectedTimestamps);
}
private static void assertBatteryDiffEntry(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
index 20799d4..efce44e 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtilsTest.java
@@ -261,7 +261,7 @@
public void getAppUsageStartTimestampOfUser_emptyCursorContent_returnEarliestTimestamp() {
final MatrixCursor cursor =
new MatrixCursor(new String[] {AppUsageEventEntity.KEY_TIMESTAMP});
- DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
final long earliestTimestamp = 10001L;
assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
@@ -270,7 +270,7 @@
@Test
public void getAppUsageStartTimestampOfUser_nullCursor_returnEarliestTimestamp() {
- DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> null;
+ DatabaseUtils.sFakeSupplier = () -> null;
final long earliestTimestamp = 10001L;
assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
mContext, /*userId=*/ 0, earliestTimestamp)).isEqualTo(earliestTimestamp);
@@ -283,7 +283,7 @@
new MatrixCursor(new String[] {AppUsageEventEntity.KEY_TIMESTAMP});
// Adds fake data into the cursor.
cursor.addRow(new Object[] {returnedTimestamp});
- DatabaseUtils.sFakeAppUsageLatestTimestampSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
final long earliestTimestamp1 = 1001L;
assertThat(DatabaseUtils.getAppUsageStartTimestampOfUser(
@@ -302,7 +302,7 @@
AppUsageEventEntity.KEY_PACKAGE_NAME,
AppUsageEventEntity.KEY_TIMESTAMP,
AppUsageEventEntity.KEY_APP_USAGE_EVENT_TYPE});
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
assertThat(DatabaseUtils.getAppUsageEventForUsers(
mContext,
@@ -313,7 +313,7 @@
@Test
public void getAppUsageEventForUsers_nullCursor_returnEmptyMap() {
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> null;
+ DatabaseUtils.sFakeSupplier = () -> null;
assertThat(DatabaseUtils.getAppUsageEventForUsers(
mContext,
/*calendar=*/ null,
@@ -335,7 +335,7 @@
cursor.addRow(new Object[] {101L, "app name2", timestamp2});
cursor.addRow(new Object[] {101L, "app name3", timestamp2});
cursor.addRow(new Object[] {101L, "app name4", timestamp2});
- DatabaseUtils.sFakeAppUsageEventSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
final List<AppUsageEvent> appUsageEventList = DatabaseUtils.getAppUsageEventForUsers(
mContext,
@@ -356,7 +356,7 @@
BatteryHistEntry.KEY_UID,
BatteryHistEntry.KEY_USER_ID,
BatteryHistEntry.KEY_TIMESTAMP});
- DatabaseUtils.sFakeBatteryStateSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null)).isEmpty();
@@ -364,7 +364,7 @@
@Test
public void getHistoryMapSinceLastFullCharge_nullCursor_returnEmptyMap() {
- DatabaseUtils.sFakeBatteryStateSupplier = () -> null;
+ DatabaseUtils.sFakeSupplier = () -> null;
assertThat(DatabaseUtils.getHistoryMapSinceLastFullCharge(
mContext, /*calendar=*/ null)).isEmpty();
}
@@ -383,7 +383,7 @@
"app name3", timestamp2, 3, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
cursor.addRow(new Object[] {
"app name4", timestamp2, 4, ConvertUtils.CONSUMER_TYPE_UID_BATTERY});
- DatabaseUtils.sFakeBatteryStateSupplier = () -> cursor;
+ DatabaseUtils.sFakeSupplier = () -> cursor;
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge(
@@ -413,7 +413,7 @@
doReturn(true).when(mUserManager).isManagedProfile();
doReturn(UserHandle.SYSTEM).when(mUserManager).getProfileParent(UserHandle.CURRENT);
- DatabaseUtils.sFakeBatteryStateSupplier = () -> getMatrixCursor();
+ DatabaseUtils.sFakeSupplier = () -> getMatrixCursor();
final Map<Long, Map<String, BatteryHistEntry>> batteryHistMap =
DatabaseUtils.getHistoryMapSinceLastFullCharge(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TimestampUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TimestampUtilsTest.java
new file mode 100644
index 0000000..23787c7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TimestampUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.TimeZone;
+
+@RunWith(RobolectricTestRunner.class)
+public class TimestampUtilsTest {
+
+ @Before
+ public void setUp() {
+ TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
+ }
+
+ @Test
+ public void getNextHourTimestamp_returnExpectedResult() {
+ // 2021-02-28 06:00:00 => 2021-02-28 07:00:00
+ assertThat(TimestampUtils.getNextHourTimestamp(1614463200000L))
+ .isEqualTo(1614466800000L);
+ // 2021-12-31 23:59:59 => 2022-01-01 00:00:00
+ assertThat(TimestampUtils.getNextHourTimestamp(16409663999999L))
+ .isEqualTo(16409664000000L);
+ }
+
+ @Test
+ public void getNextEvenHourTimestamp_returnExpectedResult() {
+ // 2021-02-28 06:00:00 => 2021-02-28 08:00:00
+ assertThat(TimestampUtils.getNextEvenHourTimestamp(1614463200000L))
+ .isEqualTo(1614470400000L);
+ // 2021-12-31 23:59:59 => 2022-01-01 00:00:00
+ assertThat(TimestampUtils.getNextEvenHourTimestamp(16409663999999L))
+ .isEqualTo(16409664000000L);
+ }
+
+ @Test
+ public void getLastEvenHourTimestamp_returnExpectedResult() {
+ // 2021-02-28 06:00:06 => 2021-02-28 06:00:00
+ assertThat(TimestampUtils.getLastEvenHourTimestamp(1614463206000L))
+ .isEqualTo(1614463200000L);
+ // 2021-12-31 23:59:59 => 2021-12-31 22:00:00
+ assertThat(TimestampUtils.getLastEvenHourTimestamp(16409663999999L))
+ .isEqualTo(16409656800000L);
+ }
+
+ @Test
+ public void getTimestampOfNextDay_returnExpectedResult() {
+ // 2021-02-28 06:00:00 => 2021-03-01 00:00:00
+ assertThat(TimestampUtils.getNextDayTimestamp(1614463200000L))
+ .isEqualTo(1614528000000L);
+ // 2021-12-31 16:00:00 => 2022-01-01 00:00:00
+ assertThat(TimestampUtils.getNextDayTimestamp(1640937600000L))
+ .isEqualTo(1640966400000L);
+ }
+
+ @Test
+ public void isMidnight_returnExpectedResult() {
+ // 2022-01-01 00:00:00
+ assertThat(TimestampUtils.isMidnight(1640966400000L)).isTrue();
+ // 2022-01-01 01:00:05
+ assertThat(TimestampUtils.isMidnight(1640970005000L)).isFalse();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
index 0124551..f510994 100644
--- a/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
+++ b/tests/robotests/src/com/android/settings/support/SupportDashboardActivityTest.java
@@ -16,6 +16,8 @@
package com.android.settings.support;
+import static com.android.settings.support.SupportDashboardActivity.ACTION_SUPPORT_SETTINGS;
+
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
@@ -59,7 +61,7 @@
mContext.getString(R.string.page_tab_title_support));
assertThat(value.intentTargetPackage).isEqualTo(mContext.getPackageName());
assertThat(value.intentTargetClass).isEqualTo(SupportDashboardActivity.class.getName());
- assertThat(value.intentAction).isEqualTo(Intent.ACTION_MAIN);
+ assertThat(value.intentAction).isEqualTo(ACTION_SUPPORT_SETTINGS);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
index ad086b7..faaa0fa 100644
--- a/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/WifiConfigController2Test.java
@@ -470,7 +470,7 @@
private void checkSavedMacRandomizedValue(int macRandomizedValue) {
when(mWifiEntry.isSaved()).thenReturn(true);
- final WifiConfiguration mockWifiConfig = mock(WifiConfiguration.class);
+ final WifiConfiguration mockWifiConfig = spy(new WifiConfiguration());
when(mockWifiConfig.getIpConfiguration()).thenReturn(mock(IpConfiguration.class));
when(mWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfig);
mockWifiConfig.macRandomizationSetting = macRandomizedValue;
@@ -809,9 +809,10 @@
private void setUpModifyingSavedPeapConfigController() {
when(mWifiEntry.isSaved()).thenReturn(true);
when(mWifiEntry.getSecurity()).thenReturn(WifiEntry.SECURITY_EAP);
- final WifiConfiguration mockWifiConfig = mock(WifiConfiguration.class);
+ final WifiConfiguration mockWifiConfig = spy(new WifiConfiguration());
+ mockWifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
when(mockWifiConfig.getIpConfiguration()).thenReturn(mock(IpConfiguration.class));
- final WifiEnterpriseConfig mockWifiEnterpriseConfig = mock(WifiEnterpriseConfig.class);
+ final WifiEnterpriseConfig mockWifiEnterpriseConfig = spy(new WifiEnterpriseConfig());
when(mockWifiEnterpriseConfig.getEapMethod()).thenReturn(Eap.PEAP);
mockWifiConfig.enterpriseConfig = mockWifiEnterpriseConfig;
when(mWifiEntry.getWifiConfiguration()).thenReturn(mockWifiConfig);
@@ -980,8 +981,8 @@
private void setUpModifyingSavedCertificateConfigController(String savedCaCertificate,
String savedUserCertificate) {
- final WifiConfiguration mockWifiConfig = mock(WifiConfiguration.class);
- final WifiEnterpriseConfig mockWifiEnterpriseConfig = mock(WifiEnterpriseConfig.class);
+ final WifiConfiguration mockWifiConfig = spy(new WifiConfiguration());
+ final WifiEnterpriseConfig mockWifiEnterpriseConfig = spy(new WifiEnterpriseConfig());
mockWifiConfig.enterpriseConfig = mockWifiEnterpriseConfig;
when(mWifiEntry.isSaved()).thenReturn(true);
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
index 0c2bbf5..fb64023 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -156,6 +156,16 @@
}
@Test
+ public void onSecuritySummaryChanged_canNotShowWifiHotspot_returnFalse() {
+ int stringResId = R.string.wifi_security_sae;
+ mWifiTetherSettings.mWifiHotspotSecurity = mock(Preference.class);
+
+ mWifiTetherSettings.onSecuritySummaryChanged(stringResId);
+
+ verify(mWifiTetherSettings.mWifiHotspotSecurity).setSummary(stringResId);
+ }
+
+ @Test
public void onSpeedSummaryChanged_canNotShowWifiHotspot_returnFalse() {
int stringResId = R.string.wifi_hotspot_speed_summary_6g;
mWifiTetherSettings.mWifiHotspotSpeed = mock(Preference.class);
diff --git a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java
index cffc452..ae32651 100644
--- a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java
+++ b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.java
@@ -33,6 +33,7 @@
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -54,18 +55,25 @@
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String ACTIVITY_CLASS_NAME =
"com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity";
- public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
+ private static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
+ private static final String EXTRA_SKIP_FIND_SENSOR = "skip_find_sensor";
private static final String EXTRA_FROM_SETTINGS_SUMMARY = "from_settings_summary";
private static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type";
private static final String EXTRA_KEY_GK_PW_HANDLE = "gk_pw_handle";
private static final String TEST_PIN = "1234";
+ private static final String UDFPS_ENROLLING_TITLE = "Touch & hold the fingerprint sensor";
+ private static final String SFPS_ENROLLING_TITLE =
+ "Lift, then touch. Move your finger slightly each time.";
+ private static final String RFPS_ENROLLING_TITLE = "Lift, then touch again";
+
private UiDevice mDevice;
private byte[] mToken = new byte[]{};
private Context mContext;
private boolean mFingerprintPropCallbackLaunched;
private boolean mCanAssumeUdfps;
private boolean mCanAssumeSfps;
+ private String mEnrollingTitle;
private static final int IDLE_TIMEOUT = 10000;
@@ -94,6 +102,13 @@
final FingerprintSensorPropertiesInternal prop = list.get(0);
mCanAssumeUdfps = prop.isAnyUdfpsType();
mCanAssumeSfps = prop.isAnySidefpsType();
+ if (mCanAssumeUdfps) {
+ mEnrollingTitle = UDFPS_ENROLLING_TITLE;
+ } else if (mCanAssumeSfps) {
+ mEnrollingTitle = SFPS_ENROLLING_TITLE;
+ } else {
+ mEnrollingTitle = RFPS_ENROLLING_TITLE;
+ }
}
});
@@ -106,8 +121,9 @@
}
@Test
- public void testLaunchChooseLock() {
- launchFingerprintEnrollActivity(false, null);
+ public void testIntroChooseLock() {
+ final Intent intent = newActivityIntent();
+ mContext.startActivity(intent);
assertThat(mDevice.wait(Until.hasObject(By.text("Choose your backup screen lock method")),
IDLE_TIMEOUT)).isTrue();
}
@@ -135,12 +151,12 @@
}
@Test
- public void testLaunchWithGkPwHandle_withUdfps_clickStart() {
+ public void testIntroWithGkPwHandle_withUdfps_clickStart() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -159,17 +175,16 @@
startBtn.click();
// Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text("Touch & hold the fingerprint sensor")),
- IDLE_TIMEOUT)).isTrue();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
- public void testLaunchWithGkPwHandle_withUdfps_clickLottie() {
+ public void testIntroWithGkPwHandle_withUdfps_clickLottie() {
assumeTrue(mCanAssumeUdfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -188,17 +203,16 @@
lottie.click();
// Enrolling page
- assertThat(mDevice.wait(Until.hasObject(By.text("Touch & hold the fingerprint sensor")),
- IDLE_TIMEOUT)).isTrue();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
}
@Test
- public void testLaunchWithGkPwHandle_withSfps() {
+ public void testIntroWithGkPwHandle_withSfps() {
assumeTrue(mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -216,12 +230,12 @@
}
@Test
- public void testLaunchWithGkPwHandle_withRfps() {
+ public void testIntroWithGkPwHandle_withRfps() {
assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -241,10 +255,10 @@
}
@Test
- public void testLaunchWithGkPwHandle_clickNoThanksInIntroPage() {
+ public void testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -258,10 +272,10 @@
}
@Test
- public void testLaunchWithGkPwHandle_clickSkipInFindSensor() {
+ public void testIntroWithGkPwHandle_clickSkipInFindSensor() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(false);
+ launchIntroWithGkPwHandle(false);
// Intro page
verifyIntroPage();
@@ -282,10 +296,10 @@
}
@Test
- public void testLaunchWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
+ public void testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(true);
+ launchIntroWithGkPwHandle(true);
// Intro page
verifyIntroPage();
@@ -315,10 +329,10 @@
}
@Test
- public void testLaunchWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
+ public void testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivityWithGkPwHandle(true);
+ launchIntroWithGkPwHandle(true);
// Intro page
verifyIntroPage();
@@ -346,43 +360,138 @@
}
@Test
- public void testLaunchCheckPin() {
+ public void testIntroCheckPin() {
LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
- launchFingerprintEnrollActivity(false, null);
+ final Intent intent = newActivityIntent();
+ mContext.startActivity(intent);
assertThat(mDevice.wait(Until.hasObject(By.text("Enter your device PIN to continue")),
IDLE_TIMEOUT)).isTrue();
}
+ @Test
+ public void testEnrollingWithGkPwHandle() {
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
+
+ launchEnrollingWithGkPwHandle();
+
+ // Enrolling screen
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
+ }
+
+ @Test
+ public void testEnrollingIconTouchDialog_withSfps() {
+ assumeTrue(mCanAssumeSfps);
+
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
+
+ launchEnrollingWithGkPwHandle();
+
+ // Enrolling screen
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
+
+ final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
+ "illustration_lottie"));
+ assertThat(lottie).isNotNull();
+
+ lottie.click();
+ lottie.click();
+ lottie.click();
+
+ // IconTouchDialog
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text("Touch the sensor instead")), IDLE_TIMEOUT))
+ .isTrue();
+ final UiObject2 okButton = mDevice.findObject(By.text("OK"));
+ assertThat(okButton).isNotNull();
+
+ okButton.click();
+
+ // Enrolling screen again
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
+ }
+
+ @Test
+ public void testEnrollingIconTouchDialog_withRfps() {
+ assumeFalse(mCanAssumeUdfps || mCanAssumeSfps);
+
+ LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true);
+
+ launchEnrollingWithGkPwHandle();
+
+ // Enrolling screen
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
+
+ final UiObject2 lottie = mDevice.findObject(By.res(SETTINGS_PACKAGE_NAME,
+ "fingerprint_progress_bar"));
+ assertThat(lottie).isNotNull();
+
+ lottie.click();
+ lottie.click();
+ lottie.click();
+
+ // IconTouchDialog
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text("Whoops, that\u2019s not the sensor")),
+ IDLE_TIMEOUT)).isTrue();
+ final UiObject2 okButton = mDevice.findObject(By.text("OK"));
+ assertThat(okButton).isNotNull();
+
+ okButton.click();
+
+ // Enrolling screen again
+ mDevice.waitForIdle();
+ assertThat(mDevice.wait(Until.hasObject(By.text(mEnrollingTitle)), IDLE_TIMEOUT)).isTrue();
+ }
+
@After
public void tearDown() throws Exception {
LockScreenUtil.resetLockscreen(TEST_PIN);
mDevice.pressHome();
}
- private void launchFingerprintEnrollActivityWithGkPwHandle(boolean isSuw) {
+ private void launchIntroWithGkPwHandle(boolean isSuw) {
LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
final int userId = UserHandle.myUserId();
final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
- launchFingerprintEnrollActivity(isSuw, response.getGatekeeperPasswordHandle());
+ final Intent intent = newActivityIntent();
+ if (isSuw) {
+ intent.putExtra(EXTRA_IS_SETUP_FLOW, true);
+ }
+ intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
+ mContext.startActivity(intent);
};
LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
}
- private void launchFingerprintEnrollActivity(boolean isSuw, Long gkPwHandle) {
+ private void launchEnrollingWithGkPwHandle() {
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ final LockscreenCredential lockscreenCredential = LockscreenCredential.createPin(TEST_PIN);
+ final int userId = UserHandle.myUserId();
+ final LockPatternChecker.OnVerifyCallback onVerifyCallback = (response, timeoutMs) -> {
+ final Intent intent = newActivityIntent();
+ intent.putExtra(EXTRA_SKIP_FIND_SENSOR, true);
+ intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, response.getGatekeeperPasswordHandle());
+ mContext.startActivity(intent);
+ };
+ LockPatternChecker.verifyCredential(lockPatternUtils, lockscreenCredential,
+ userId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE, onVerifyCallback);
+ }
+
+ @NonNull
+ private Intent newActivityIntent() {
Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME, ACTIVITY_CLASS_NAME);
- if (isSuw) {
- intent.putExtra(EXTRA_IS_SETUP_FLOW, true);
- }
intent.putExtra(EXTRA_FROM_SETTINGS_SUMMARY, true);
intent.putExtra(EXTRA_PAGE_TRANSITION_TYPE, 1);
intent.putExtra(Intent.EXTRA_USER_ID, mContext.getUserId());
- if (gkPwHandle != null) {
- intent.putExtra(EXTRA_KEY_GK_PW_HANDLE, gkPwHandle);
- }
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
+ return intent;
+
}
}
diff --git a/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
index 395f88f..90652f0 100644
--- a/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
+++ b/tests/unit/src/com/android/settings/biometrics/BiometricNavigationUtilsTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -33,6 +34,7 @@
import android.os.UserManager;
import android.provider.Settings;
+import androidx.activity.result.ActivityResultLauncher;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -55,6 +57,8 @@
@Mock
private UserManager mUserManager;
+ @Mock
+ private ActivityResultLauncher<Intent> mLauncher;
private Context mContext;
private BiometricNavigationUtils mBiometricNavigationUtils;
@@ -72,7 +76,7 @@
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
- Bundle.EMPTY);
+ Bundle.EMPTY, null);
assertQuietModeDialogLaunchRequested();
}
@@ -82,7 +86,17 @@
when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
- mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isFalse();
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isFalse();
+ }
+
+ @Test
+ public void launchBiometricSettings_quietMode_withLauncher_notThroughLauncher() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(true);
+
+ mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME,
+ Bundle.EMPTY, mLauncher);
+
+ verify(mLauncher, never()).launch(any(Intent.class));
}
@Test
@@ -90,7 +104,7 @@
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
mBiometricNavigationUtils.launchBiometricSettings(
- mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY);
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null);
assertSettingsPageLaunchRequested(false /* shouldContainExtras */);
}
@@ -100,7 +114,7 @@
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
- mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY)).isTrue();
+ mContext, SETTINGS_CLASS_NAME, Bundle.EMPTY, null)).isTrue();
}
@Test
@@ -108,17 +122,29 @@
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
final Bundle extras = createNotEmptyExtras();
- mBiometricNavigationUtils.launchBiometricSettings(mContext, SETTINGS_CLASS_NAME, extras);
+ mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, extras, null);
assertSettingsPageLaunchRequested(true /* shouldContainExtras */);
}
@Test
+ public void launchBiometricSettings_noQuietMode_withLauncher_launchesThroughLauncher() {
+ when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
+
+ final Bundle extras = createNotEmptyExtras();
+ mBiometricNavigationUtils.launchBiometricSettings(
+ mContext, SETTINGS_CLASS_NAME, extras, mLauncher);
+
+ verify(mLauncher).launch(any(Intent.class));
+ }
+
+ @Test
public void launchBiometricSettings_noQuietMode_withExtras_returnsTrue() {
when(mUserManager.isQuietModeEnabled(any())).thenReturn(false);
assertThat(mBiometricNavigationUtils.launchBiometricSettings(
- mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras())).isTrue();
+ mContext, SETTINGS_CLASS_NAME, createNotEmptyExtras(), null)).isTrue();
}
@Test
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModelTest.java
index 4de1057..fb0937e 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollEnrollingViewModelTest.java
@@ -21,7 +21,6 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_ULTRASONIC;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.ErrorDialogData;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_BACK_PRESSED;
import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollEnrollingViewModel.FINGERPRINT_ENROLL_ENROLLING_CANCELED_BECAUSE_USER_SKIP;
@@ -103,10 +102,6 @@
mViewModel.showIconTouchDialog();
assertThat(actionLiveData.getValue()).isEqualTo(
FINGERPRINT_ENROLL_ENROLLING_ACTION_SHOW_ICON_TOUCH_DIALOG);
-
- mViewModel.onIconTouchDialogDismiss();
- assertThat(actionLiveData.getValue()).isEqualTo(
- FINGERPRINT_ENROLL_ENROLLING_ACTION_DISMISS_ICON_TOUCH_DIALOG);
}
@Test
diff --git a/tests/unit/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceControllerTest.java b/tests/unit/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceControllerTest.java
new file mode 100644
index 0000000..f019793
--- /dev/null
+++ b/tests/unit/src/com/android/settings/regionalpreferences/RegionalFooterPreferenceControllerTest.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2023 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.regionalpreferences;
+
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.widget.FooterPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class RegionalFooterPreferenceControllerTest {
+
+ private static String KEY_FOOTER_PREFERENCE = "regional_pref_footer";
+ private Context mContext;
+ private RegionalFooterPreferenceController mRegionalFooterPreferenceController;
+
+ @Mock
+ private FooterPreference mMockFooterPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ mContext = ApplicationProvider.getApplicationContext();
+ mRegionalFooterPreferenceController = new RegionalFooterPreferenceController(mContext,
+ KEY_FOOTER_PREFERENCE);
+ }
+
+ @Test
+ public void setupFooterPreference_shouldSetAsTextInLearnMore() {
+ mRegionalFooterPreferenceController.setupFooterPreference(mMockFooterPreference);
+ verify(mMockFooterPreference).setLearnMoreText(anyString());
+ }
+}
diff --git a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
index b75f9fc..dbbdfec 100644
--- a/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
+++ b/tests/unit/src/com/android/settings/wifi/repository/WifiHotspotRepositoryTest.java
@@ -18,7 +18,9 @@
import static android.net.wifi.SoftApConfiguration.BAND_2GHZ;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION;
import static android.net.wifi.WifiAvailableChannel.OP_MODE_SAP;
import static com.android.settings.wifi.repository.WifiHotspotRepository.BAND_2GHZ_5GHZ;
@@ -32,6 +34,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -81,6 +84,8 @@
@Mock
WifiManager mWifiManager;
@Mock
+ MutableLiveData<Integer> mSecurityType;
+ @Mock
MutableLiveData<Integer> mSpeedType;
WifiHotspotRepository mWifiHotspotRepository;
@@ -93,6 +98,7 @@
doReturn(SPEED_6GHZ).when(mSpeedType).getValue();
mWifiHotspotRepository = new WifiHotspotRepository(mContext, mWifiManager);
+ mWifiHotspotRepository.mSecurityType = mSecurityType;
mWifiHotspotRepository.mSpeedType = mSpeedType;
mWifiHotspotRepository.mCurrentCountryCode = WIFI_CURRENT_COUNTRY_CODE;
mWifiHotspotRepository.mIsDualBand = true;
@@ -156,6 +162,7 @@
@Test
public void refresh_liveDataNotUsed_doNothing() {
// If LiveData is not used then it's null.
+ mWifiHotspotRepository.mSecurityType = null;
mWifiHotspotRepository.mSpeedType = null;
mWifiHotspotRepository.refresh();
@@ -169,7 +176,7 @@
mWifiHotspotRepository.refresh();
- verify(mWifiManager).getSoftApConfiguration();
+ verify(mWifiManager, atLeast(1)).getSoftApConfiguration();
verify(mSpeedType).setValue(anyInt());
}
@@ -193,6 +200,105 @@
@Test
@UiThreadTest
+ public void getSecurityType_shouldNotReturnNull() {
+ // If LiveData is not used then it's null.
+ mWifiHotspotRepository.mSecurityType = null;
+ mockConfigSecurityType(SECURITY_TYPE_OPEN);
+
+ assertThat(mWifiHotspotRepository.getSecurityType()).isNotNull();
+ }
+
+ @Test
+ public void updateSecurityType_securityTypeOpen_setValueCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_OPEN);
+
+ mWifiHotspotRepository.updateSecurityType();
+
+ verify(mSecurityType).setValue(SECURITY_TYPE_OPEN);
+ }
+
+ @Test
+ public void updateSecurityType_securityTypeWpa2_setValueCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA2_PSK);
+
+ mWifiHotspotRepository.updateSecurityType();
+
+ verify(mSecurityType).setValue(SECURITY_TYPE_WPA2_PSK);
+ }
+
+ @Test
+ public void updateSecurityType_securityTypeWpa2Wpa3_setValueCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+
+ mWifiHotspotRepository.updateSecurityType();
+
+ verify(mSecurityType).setValue(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ }
+
+ @Test
+ public void updateSecurityType_securityTypeWpa3_setValueCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA3_SAE);
+
+ mWifiHotspotRepository.updateSecurityType();
+
+ verify(mSecurityType).setValue(SECURITY_TYPE_WPA3_SAE);
+ }
+
+ @Test
+ public void setSecurityType_sameValue_doNotSetConfig() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA3_SAE);
+
+ mWifiHotspotRepository.setSecurityType(SECURITY_TYPE_WPA3_SAE);
+
+ verify(mWifiManager, never()).setSoftApConfiguration(any());
+ }
+
+ @Test
+ public void setSecurityType_wpa3ToWpa2Wpa3_setConfigCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA3_SAE);
+
+ mWifiHotspotRepository.setSecurityType(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ }
+
+ @Test
+ public void setSecurityType_Wpa2Wpa3ToWpa2_setConfigCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+
+ mWifiHotspotRepository.setSecurityType(SECURITY_TYPE_WPA2_PSK);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_WPA2_PSK);
+ }
+
+ @Test
+ public void setSecurityType_Wpa2ToOpen_setConfigCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_WPA2_PSK);
+
+ mWifiHotspotRepository.setSecurityType(SECURITY_TYPE_OPEN);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_OPEN);
+ }
+
+ @Test
+ public void setSecurityType_OpenToWpa3_setConfigCorrectly() {
+ mockConfigSecurityType(SECURITY_TYPE_OPEN);
+
+ mWifiHotspotRepository.setSecurityType(SECURITY_TYPE_WPA3_SAE);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_WPA3_SAE);
+ }
+
+ @Test
+ @UiThreadTest
public void getSpeedType_shouldNotReturnNull() {
// If LiveData is not used then it's null.
mWifiHotspotRepository.mSpeedType = null;
@@ -312,8 +418,8 @@
}
@Test
- public void setSpeedType_from2g5ghz_setConfigBandTo6ghzPreferred() {
- mockGetSoftApConfiguration(SPEED_2GHZ_5GHZ);
+ public void setSpeedType_2g5ghzTo6ghz_setConfigBandTo6ghzPreferred() {
+ mockConfigSpeedType(SPEED_2GHZ_5GHZ);
mWifiHotspotRepository.setSpeedType(SPEED_6GHZ);
@@ -322,8 +428,19 @@
}
@Test
- public void setSpeedType_from6ghz_setConfigBandsTo2g5ghz() {
- mockGetSoftApConfiguration(SPEED_6GHZ);
+ public void setSpeedType_2g5ghzTo6ghz_setConfigSecurityToWpa3() {
+ mockConfig(SPEED_2GHZ_5GHZ, SECURITY_TYPE_WPA3_SAE_TRANSITION);
+
+ mWifiHotspotRepository.setSpeedType(SPEED_6GHZ);
+
+ verify(mWifiManager).setSoftApConfiguration(mSoftApConfigCaptor.capture());
+ assertThat(mSoftApConfigCaptor.getValue().getSecurityType())
+ .isEqualTo(SECURITY_TYPE_WPA3_SAE);
+ }
+
+ @Test
+ public void setSpeedType_6ghzTo2g5ghz_setConfigBandsTo2g5ghz() {
+ mockConfigSpeedType(SPEED_6GHZ);
mWifiHotspotRepository.mIsDualBand = true;
mWifiHotspotRepository.setSpeedType(SPEED_2GHZ_5GHZ);
@@ -335,8 +452,8 @@
}
@Test
- public void setSpeedType_from2ghz_setConfigBandTo5ghzPreferred() {
- mockGetSoftApConfiguration(SPEED_2GHZ);
+ public void setSpeedType_2ghzTo5ghz_setConfigBandTo5ghzPreferred() {
+ mockConfigSpeedType(SPEED_2GHZ);
mWifiHotspotRepository.setSpeedType(SPEED_5GHZ);
@@ -345,8 +462,8 @@
}
@Test
- public void setSpeedType_from5ghz_setConfigBandTo6ghzPreferred() {
- mockGetSoftApConfiguration(SPEED_5GHZ);
+ public void setSpeedType_5ghzTo6ghz_setConfigBandTo6ghzPreferred() {
+ mockConfigSpeedType(SPEED_5GHZ);
mWifiHotspotRepository.setSpeedType(SPEED_6GHZ);
@@ -355,8 +472,8 @@
}
@Test
- public void setSpeedType_from5gTo6ghz_setConfigBandTo2ghz() {
- mockGetSoftApConfiguration(SPEED_6GHZ);
+ public void setSpeedType_6ghzTo2ghz_setConfigBandTo2ghz() {
+ mockConfigSpeedType(SPEED_6GHZ);
mWifiHotspotRepository.setSpeedType(SPEED_2GHZ);
@@ -475,10 +592,24 @@
assertThat(mWifiHotspotRepository.get6gAvailable()).isNotNull();
}
- private void mockGetSoftApConfiguration(int speedType) {
+ private void mockConfigSecurityType(int securityType) {
+ mockConfig(securityType, SPEED_2GHZ);
+ }
+
+ private void mockConfigSpeedType(int speedType) {
+ mockConfig(SECURITY_TYPE_WPA3_SAE, speedType);
+ }
+
+ private void mockConfig(int securityType, int speedType) {
+ SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
+ // Security Type
+ doReturn(securityType).when(mSecurityType).getValue();
+ String passphrase = (securityType == SECURITY_TYPE_OPEN) ? null : WIFI_PASSWORD;
+ configBuilder.setPassphrase(passphrase, securityType).build();
+
+ // Speed Type
doReturn(speedType).when(mSpeedType).getValue();
mWifiHotspotRepository.mIsDualBand = true;
- SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
if (speedType == SPEED_2GHZ) {
mWifiHotspotRepository.mIsDualBand = false;
configBuilder.setBand(BAND_2GHZ);
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettingsTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettingsTest.java
new file mode 100644
index 0000000..511240e
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecuritySettingsTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION;
+
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_NONE;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA2;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA2_WPA3;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA3;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WifiHotspotSecuritySettingsTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Mock
+ WifiHotspotSecurityViewModel mViewModel;
+ @Mock
+ SelectorWithWidgetPreference mRadioButtonWpa3;
+ @Mock
+ SelectorWithWidgetPreference mRadioButtonWpa2Wpa3;
+ @Mock
+ SelectorWithWidgetPreference mRadioButtonWpa2;
+ @Mock
+ SelectorWithWidgetPreference mRadioButtonNone;
+
+ Map<Integer, WifiHotspotSecurityViewModel.ViewItem> mViewItemMap = new HashMap<>();
+ WifiHotspotSecurityViewModel.ViewItem mViewItemWpa3 =
+ new WifiHotspotSecurityViewModel.ViewItem(KEY_SECURITY_WPA3);
+ WifiHotspotSecurityViewModel.ViewItem mViewItemWpa2Wpa3 =
+ new WifiHotspotSecurityViewModel.ViewItem(KEY_SECURITY_WPA2_WPA3);
+ WifiHotspotSecurityViewModel.ViewItem mViewItemWpa2 =
+ new WifiHotspotSecurityViewModel.ViewItem(KEY_SECURITY_WPA2);
+ WifiHotspotSecurityViewModel.ViewItem mViewItemNone =
+ new WifiHotspotSecurityViewModel.ViewItem(KEY_SECURITY_NONE);
+
+ WifiHotspotSecuritySettings mSettings;
+
+ @Before
+ public void setUp() {
+ mViewItemMap.put(SECURITY_TYPE_WPA3_SAE, mViewItemWpa3);
+ mViewItemMap.put(SECURITY_TYPE_WPA3_SAE_TRANSITION, mViewItemWpa2Wpa3);
+ mViewItemMap.put(SECURITY_TYPE_WPA2_PSK, mViewItemWpa2);
+ mViewItemMap.put(SECURITY_TYPE_OPEN, mViewItemNone);
+
+ when(mRadioButtonWpa3.getKey()).thenReturn(KEY_SECURITY_WPA3);
+ when(mRadioButtonWpa2Wpa3.getKey()).thenReturn(KEY_SECURITY_WPA2_WPA3);
+ when(mRadioButtonWpa2.getKey()).thenReturn(KEY_SECURITY_WPA2);
+ when(mRadioButtonNone.getKey()).thenReturn(KEY_SECURITY_NONE);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(
+ () -> mSettings = spy(new WifiHotspotSecuritySettings()));
+ mSettings.mWifiHotspotSecurityViewModel = mViewModel;
+ when(mSettings.findPreference(KEY_SECURITY_WPA3)).thenReturn(mRadioButtonWpa3);
+ when(mSettings.findPreference(KEY_SECURITY_WPA2_WPA3)).thenReturn(mRadioButtonWpa2Wpa3);
+ when(mSettings.findPreference(KEY_SECURITY_WPA2)).thenReturn(mRadioButtonWpa2);
+ when(mSettings.findPreference(KEY_SECURITY_NONE)).thenReturn(mRadioButtonNone);
+ }
+
+ @Test
+ public void onViewItemListDataChanged_checkedWpa3_setViewItemCorrectly() {
+ mViewItemWpa3.mIsChecked = true;
+
+ mSettings.onViewItemListDataChanged(mViewItemMap.values().stream().toList());
+
+ verify(mRadioButtonWpa3).setChecked(true);
+ }
+
+ @Test
+ public void onViewItemListDataChanged_checkedWpa2Wpa3_setViewItemCorrectly() {
+ mViewItemWpa2Wpa3.mIsChecked = true;
+
+ mSettings.onViewItemListDataChanged(mViewItemMap.values().stream().toList());
+
+ verify(mRadioButtonWpa2Wpa3).setChecked(true);
+ }
+
+ @Test
+ public void onViewItemListDataChanged_checkedWpa2_setViewItemCorrectly() {
+ mViewItemWpa2.mIsChecked = true;
+
+ mSettings.onViewItemListDataChanged(mViewItemMap.values().stream().toList());
+
+ verify(mRadioButtonWpa2).setChecked(true);
+ }
+
+ @Test
+ public void onViewItemListDataChanged_checkedNone_setViewItemCorrectly() {
+ mViewItemNone.mIsChecked = true;
+
+ mSettings.onViewItemListDataChanged(mViewItemMap.values().stream().toList());
+
+ verify(mRadioButtonNone).setChecked(true);
+ }
+
+ @Test
+ public void onViewItemListDataChanged_enabledWpa3Only_setViewItemCorrectly() {
+ when(mRadioButtonWpa2Wpa3.isEnabled()).thenReturn(true);
+ when(mRadioButtonWpa2.isEnabled()).thenReturn(true);
+ when(mRadioButtonNone.isEnabled()).thenReturn(true);
+ mViewItemWpa2Wpa3.mIsEnabled = false;
+ mViewItemWpa2.mIsEnabled = false;
+ mViewItemNone.mIsEnabled = false;
+
+ mSettings.onViewItemListDataChanged(mViewItemMap.values().stream().toList());
+
+ verify(mRadioButtonWpa2Wpa3).setEnabled(false);
+ verify(mRadioButtonWpa2).setEnabled(false);
+ verify(mRadioButtonNone).setEnabled(false);
+ }
+
+ @Test
+ public void onRadioButtonClicked_clickedWpa3_setSecurityTypeCorrectly() {
+ mSettings.onRadioButtonClicked(mRadioButtonWpa3);
+
+ verify(mViewModel).handleRadioButtonClicked(KEY_SECURITY_WPA3);
+ }
+
+ @Test
+ public void onRadioButtonClicked_clickedWpa2Wpa3_setSecurityTypeCorrectly() {
+ mSettings.onRadioButtonClicked(mRadioButtonWpa2Wpa3);
+
+ verify(mViewModel).handleRadioButtonClicked(KEY_SECURITY_WPA2_WPA3);
+ }
+
+ @Test
+ public void onRadioButtonClicked_clickedWpa2_setSecurityTypeCorrectly() {
+ mSettings.onRadioButtonClicked(mRadioButtonWpa2);
+
+ verify(mViewModel).handleRadioButtonClicked(KEY_SECURITY_WPA2);
+ }
+
+ @Test
+ public void onRadioButtonClicked_clickedNone_setSecurityTypeCorrectly() {
+ mSettings.onRadioButtonClicked(mRadioButtonNone);
+
+ verify(mViewModel).handleRadioButtonClicked(KEY_SECURITY_NONE);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModelTest.java
new file mode 100644
index 0000000..db768c7
--- /dev/null
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiHotspotSecurityViewModelTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.wifi.tether;
+
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_OPEN;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA2_PSK;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE;
+import static android.net.wifi.SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION;
+
+import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ;
+import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_2GHZ_5GHZ;
+import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_5GHZ;
+import static com.android.settings.wifi.repository.WifiHotspotRepository.SPEED_6GHZ;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_NONE;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA2;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA2_WPA3;
+import static com.android.settings.wifi.tether.WifiHotspotSecurityViewModel.KEY_SECURITY_WPA3;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Application;
+import android.content.Context;
+
+import androidx.lifecycle.MutableLiveData;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.wifi.repository.WifiHotspotRepository;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@UiThreadTest
+public class WifiHotspotSecurityViewModelTest {
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Spy
+ Context mContext = ApplicationProvider.getApplicationContext();
+ @Mock
+ WifiHotspotRepository mWifiHotspotRepository;
+ @Mock
+ MutableLiveData<Integer> mSecurityType;
+ @Mock
+ MutableLiveData<Integer> mSpeedType;
+
+ WifiHotspotSecurityViewModel mViewModel;
+
+ @Before
+ public void setUp() {
+ FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
+ when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository())
+ .thenReturn(mWifiHotspotRepository);
+ when(mWifiHotspotRepository.getSecurityType()).thenReturn(mSecurityType);
+ when(mWifiHotspotRepository.getSpeedType()).thenReturn(mSpeedType);
+
+ mViewModel = new WifiHotspotSecurityViewModel((Application) mContext);
+ }
+
+ @Test
+ public void constructor_observeDataAndSetAutoRefresh() {
+ verify(mSecurityType).observeForever(mViewModel.mSecurityTypeObserver);
+ verify(mSpeedType).observeForever(mViewModel.mSpeedTypeObserver);
+ }
+
+ @Test
+ public void onCleared_removeObserverData() {
+ mViewModel.onCleared();
+
+ verify(mSecurityType).removeObserver(mViewModel.mSecurityTypeObserver);
+ verify(mSpeedType).removeObserver(mViewModel.mSpeedTypeObserver);
+ }
+
+ @Test
+ public void onSecurityTypeChanged_securityTypeWpa3_setCheckedCorrectly() {
+ mViewModel.onSecurityTypeChanged(SECURITY_TYPE_WPA3_SAE);
+
+ assertItemChecked(true, false, false, false);
+ }
+
+ @Test
+ public void onSecurityTypeChanged_securityTypeWpa2Wpa3_setCheckedCorrectly() {
+ mViewModel.onSecurityTypeChanged(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+
+ assertItemChecked(false, true, false, false);
+ }
+
+ @Test
+ public void onSecurityTypeChanged_securityTypeWpa2_setCheckedCorrectly() {
+ mViewModel.onSecurityTypeChanged(SECURITY_TYPE_WPA2_PSK);
+
+ assertItemChecked(false, false, true, false);
+ }
+
+ @Test
+ public void onSecurityTypeChanged_securityTypeNone_setCheckedCorrectly() {
+ mViewModel.onSecurityTypeChanged(SECURITY_TYPE_OPEN);
+
+ assertItemChecked(false, false, false, true);
+ }
+
+ @Test
+ public void onSpeedTypeChanged_speed6g_setEnabledCorrectly() {
+ mViewModel.onSpeedTypeChanged(SPEED_6GHZ);
+
+ assertItemEnabled(true, false, false, false);
+ }
+
+ @Test
+ public void onSpeedTypeChanged_speed2g5g_setEnabledCorrectly() {
+ mViewModel.onSpeedTypeChanged(SPEED_2GHZ_5GHZ);
+
+ assertItemEnabled(true, true, true, true);
+ }
+
+ @Test
+ public void onSpeedTypeChanged_speed5g_setEnabledCorrectly() {
+ mViewModel.onSpeedTypeChanged(SPEED_5GHZ);
+
+ assertItemEnabled(true, true, true, true);
+ }
+
+ @Test
+ public void onSpeedTypeChanged_speed2g_setEnabledCorrectly() {
+ mViewModel.onSpeedTypeChanged(SPEED_2GHZ);
+
+ assertItemEnabled(true, true, true, true);
+ }
+
+ @Test
+ public void handleRadioButtonClicked_keyWpa3_setSecurityTypeCorrectly() {
+ mViewModel.handleRadioButtonClicked(KEY_SECURITY_WPA3);
+
+ verify(mWifiHotspotRepository).setSecurityType(SECURITY_TYPE_WPA3_SAE);
+ }
+
+ @Test
+ public void handleRadioButtonClicked_keyWpa2Wpa3_setSecurityTypeCorrectly() {
+ mViewModel.handleRadioButtonClicked(KEY_SECURITY_WPA2_WPA3);
+
+ verify(mWifiHotspotRepository).setSecurityType(SECURITY_TYPE_WPA3_SAE_TRANSITION);
+ }
+
+ @Test
+ public void handleRadioButtonClicked_keyWpa2_setSecurityTypeCorrectly() {
+ mViewModel.handleRadioButtonClicked(KEY_SECURITY_WPA2);
+
+ verify(mWifiHotspotRepository).setSecurityType(SECURITY_TYPE_WPA2_PSK);
+ }
+
+ @Test
+ public void handleRadioButtonClicked_keyNone_setSecurityTypeCorrectly() {
+ mViewModel.handleRadioButtonClicked(KEY_SECURITY_NONE);
+
+ verify(mWifiHotspotRepository).setSecurityType(SECURITY_TYPE_OPEN);
+ }
+
+ @Test
+ public void getViewItemListData_shouldNotReturnNull() {
+ // Reset mViewInfoListData to trigger an update
+ mViewModel.mViewInfoListData = null;
+
+ assertThat(mViewModel.getViewItemListData()).isNotNull();
+ }
+
+ private void assertItemChecked(boolean checkedWpa3, boolean checkedWpa2Wpa3,
+ boolean checkedWpa2, boolean checkedNone) {
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA3_SAE).mIsChecked)
+ .isEqualTo(checkedWpa3);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA3_SAE_TRANSITION).mIsChecked)
+ .isEqualTo(checkedWpa2Wpa3);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA2_PSK).mIsChecked)
+ .isEqualTo(checkedWpa2);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_OPEN).mIsChecked)
+ .isEqualTo(checkedNone);
+ }
+
+ private void assertItemEnabled(boolean enabledWpa3, boolean enabledWpa2Wpa3,
+ boolean enabledWpa2, boolean enabledNone) {
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA3_SAE).mIsEnabled)
+ .isEqualTo(enabledWpa3);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA3_SAE_TRANSITION).mIsEnabled)
+ .isEqualTo(enabledWpa2Wpa3);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_WPA2_PSK).mIsEnabled)
+ .isEqualTo(enabledWpa2);
+ assertThat(mViewModel.mViewItemMap.get(SECURITY_TYPE_OPEN).mIsEnabled)
+ .isEqualTo(enabledNone);
+ }
+}
diff --git a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java
index 6724dd5..4c8ce5b 100644
--- a/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java
+++ b/tests/unit/src/com/android/settings/wifi/tether/WifiTetherViewModelTest.java
@@ -52,6 +52,8 @@
@Mock
WifiHotspotRepository mWifiHotspotRepository;
@Mock
+ MutableLiveData<Integer> mSecurityType;
+ @Mock
MutableLiveData<Integer> mSpeedType;
WifiTetherViewModel mViewModel;
@@ -63,21 +65,18 @@
FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest();
when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository())
.thenReturn(mWifiHotspotRepository);
+ when(mWifiHotspotRepository.getSecurityType()).thenReturn(mSecurityType);
when(mWifiHotspotRepository.getSpeedType()).thenReturn(mSpeedType);
mViewModel = new WifiTetherViewModel(mApplication);
}
@Test
- public void constructor_setAutoRefreshTrue() {
- verify(mWifiHotspotRepository).setAutoRefresh(true);
- }
-
- @Test
public void onCleared_setAutoRefreshFalse() {
mViewModel.onCleared();
- verify(mWifiHotspotRepository).setAutoRefresh(false);
+ verify(mSecurityType).removeObserver(mViewModel.mSecurityTypeObserver);
+ verify(mSpeedType).removeObserver(mViewModel.mSpeedTypeObserver);
}
@Test
@@ -98,11 +97,23 @@
@Test
@UiThreadTest
+ public void getSecuritySummary_returnNotNull() {
+ mViewModel.mSecuritySummary = null;
+
+ mViewModel.getSecuritySummary();
+
+ assertThat(mViewModel.mSecuritySummary).isNotNull();
+ verify(mSecurityType).observeForever(mViewModel.mSecurityTypeObserver);
+ }
+
+ @Test
+ @UiThreadTest
public void getSpeedSummary_returnNotNull() {
mViewModel.mSpeedSummary = null;
mViewModel.getSpeedSummary();
assertThat(mViewModel.mSpeedSummary).isNotNull();
+ verify(mSpeedType).observeForever(mViewModel.mSpeedTypeObserver);
}
}