Merge "Reset descriptions for SeekBarPreference" into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2295ee3..0eef210 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1687,6 +1687,22 @@
</activity>
<activity
+ android:name=".Settings$LockScreenNotificationsSettingsActivity"
+ android:label="@string/lock_screen_notifs_title"
+ android:exported="true">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.LOCK_SCREEN_NOTIFICATIONS_SETTINGS" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.notification.LockScreenNotificationsPreferencePageFragment" />
+ <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+ android:value="@string/menu_key_display"/>
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity>
+
+ <activity
android:name=".Settings$BlueToothPairingActivity"
android:label="@string/bluetooth_pairing_page_title"
android:permission="android.permission.BLUETOOTH_SCAN"
@@ -5424,7 +5440,7 @@
</service>
<receiver android:name="com.android.settings.connecteddevice.audiosharing.AudioSharingReceiver"
- android:exported="false">
+ android:exported="true">
<intent-filter>
<action android:name="com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STATE_CHANGE" />
<action android:name="com.android.settings.action.BLUETOOTH_LE_AUDIO_SHARING_STOP" />
@@ -5439,6 +5455,16 @@
</intent-filter>
</receiver>
+ <receiver
+ android:name=".accessibility.HighContrastTextMigrationReceiver"
+ android:permission="android.permission.MANAGE_ACCESSIBILITY"
+ android:exported="true"> <!-- Exported for SettingsProvider restore from backup. -->
+ <intent-filter>
+ <action android:name="android.intent.action.PRE_BOOT_COMPLETED"/>
+ <action android:name="com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED"/>
+ </intent-filter>
+ </receiver>
+
<activity
android:name="Settings$ChangeNfcTagAppsActivity"
android:exported="true"
diff --git a/aconfig/settings_datetime_flag_declarations.aconfig b/aconfig/settings_datetime_flag_declarations.aconfig
index 93d41d6..9951243 100644
--- a/aconfig/settings_datetime_flag_declarations.aconfig
+++ b/aconfig/settings_datetime_flag_declarations.aconfig
@@ -8,12 +8,3 @@
description: "Enable the time feedback feature, a button to launch feedback in Date & Time Settings"
bug: "283239837"
}
-
-flag {
- name: "revamp_toggles"
- # "location" is used by the Android System Time team for feature flags.
- namespace: "location"
- description: "Makes the use location toggle dependent on automatic time zone detection"
- bug: "296835792"
-}
-
diff --git a/res/drawable/ic_settings_mouse.xml b/res/drawable/ic_settings_mouse.xml
new file mode 100644
index 0000000..4ce13d1
--- /dev/null
+++ b/res/drawable/ic_settings_mouse.xml
@@ -0,0 +1,14 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <group>
+ <clip-path
+ android:pathData="M0,0h24v24h-24z"/>
+ <path
+ android:pathData="M12,22C10.067,22 8.417,21.317 7.05,19.95C5.683,18.583 5,16.933 5,15V9C5,7.067 5.683,5.417 7.05,4.05C8.417,2.683 10.067,2 12,2C13.933,2 15.583,2.683 16.95,4.05C18.317,5.417 19,7.067 19,9V15C19,16.933 18.317,18.583 16.95,19.95C15.583,21.317 13.933,22 12,22ZM13,9H17C17,7.8 16.617,6.742 15.85,5.825C15.1,4.908 14.15,4.333 13,4.1V9ZM7,9H11V4.1C9.85,4.333 8.892,4.908 8.125,5.825C7.375,6.742 7,7.8 7,9ZM12,20C13.383,20 14.558,19.517 15.525,18.55C16.508,17.567 17,16.383 17,15V11H7V15C7,16.383 7.483,17.567 8.45,18.55C9.433,19.517 10.617,20 12,20Z"
+ android:fillColor="@android:color/white"/>
+ </group>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/pointer_icon_fill_color_background.xml b/res/drawable/pointer_icon_fill_color_background.xml
index 20deb5c..89cfe55 100644
--- a/res/drawable/pointer_icon_fill_color_background.xml
+++ b/res/drawable/pointer_icon_fill_color_background.xml
@@ -14,15 +14,217 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetBottom="@dimen/pointer_fill_style_circle_offset"
- android:insetLeft="@dimen/pointer_fill_style_circle_offset"
- android:insetRight="@dimen/pointer_fill_style_circle_offset"
- android:insetTop="@dimen/pointer_fill_style_circle_offset">
- <shape android:shape="oval">
- <size
- android:width="@dimen/pointer_fill_style_circle_inner_diameter"
- android:height="@dimen/pointer_fill_style_circle_inner_diameter" />
- <solid android:color="@android:color/white" />
- </shape>
-</inset>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" >
+ <item
+ android:state_hovered="true"
+ android:state_selected="true">
+ <layer-list>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:left="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:right="@dimen/pointer_fill_style_circle_background_outline_offset">
+ <shape android:shape="rectangle">
+ <corners
+ android:radius="@dimen/pointer_fill_style_circle_background_corner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter_less_stroke"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter_less_stroke" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_background_selected_outline_stroke"
+ android:color="@androidprv:color/materialColorSecondary" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_background_offset"
+ android:left="@dimen/pointer_fill_style_circle_background_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_background_offset"
+ android:right="@dimen/pointer_fill_style_circle_background_offset">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/pointer_fill_style_circle_background_corner_inner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter_selected"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter_selected" />
+ <solid
+ android:color="@color/pointer_fill_hovered_color" />
+ </shape>
+ </item>
+ <item
+ android:id="@+id/tintableCircleHoveredSelected"
+ android:top="@dimen/pointer_fill_style_circle_offset_selected"
+ android:left="@dimen/pointer_fill_style_circle_offset_selected"
+ android:bottom="@dimen/pointer_fill_style_circle_offset_selected"
+ android:right="@dimen/pointer_fill_style_circle_offset_selected">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_diameter"
+ android:height="@dimen/pointer_fill_style_circle_diameter" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:left="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:bottom="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:right="@dimen/pointer_fill_style_circle_outline_offset_selected">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_outline_diameter"
+ android:height="@dimen/pointer_fill_style_circle_outline_diameter" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_outline_stroke"
+ android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item android:gravity="center"
+ android:width="@dimen/pointer_fill_style_checkmark_size"
+ android:height="@dimen/pointer_fill_style_checkmark_size"
+ android:drawable="@drawable/pointer_fill_check_24dp" />
+ </layer-list>
+ </item>
+ <item android:state_selected="true">
+ <layer-list>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:left="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_background_outline_offset"
+ android:right="@dimen/pointer_fill_style_circle_background_outline_offset">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/pointer_fill_style_circle_background_corner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter_less_stroke"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter_less_stroke" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_background_selected_outline_stroke"
+ android:color="@androidprv:color/materialColorSecondary" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_background_offset"
+ android:left="@dimen/pointer_fill_style_circle_background_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_background_offset"
+ android:right="@dimen/pointer_fill_style_circle_background_offset">
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/pointer_fill_style_circle_background_corner_inner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter_selected"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter_selected" />
+ <solid android:color="@androidprv:color/materialColorSurfaceBright" />
+ </shape>
+ </item>
+ <item
+ android:id="@+id/tintableCircleSelected"
+ android:top="@dimen/pointer_fill_style_circle_offset_selected"
+ android:left="@dimen/pointer_fill_style_circle_offset_selected"
+ android:bottom="@dimen/pointer_fill_style_circle_offset_selected"
+ android:right="@dimen/pointer_fill_style_circle_offset_selected">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_diameter"
+ android:height="@dimen/pointer_fill_style_circle_diameter" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:left="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:bottom="@dimen/pointer_fill_style_circle_outline_offset_selected"
+ android:right="@dimen/pointer_fill_style_circle_outline_offset_selected">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_outline_diameter"
+ android:height="@dimen/pointer_fill_style_circle_outline_diameter" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_outline_stroke"
+ android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item
+ android:gravity="center"
+ android:width="@dimen/pointer_fill_style_checkmark_size"
+ android:height="@dimen/pointer_fill_style_checkmark_size"
+ android:drawable="@drawable/pointer_fill_check_24dp" />
+ </layer-list>
+ </item>
+ <item android:state_hovered="true">
+ <layer-list>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/pointer_fill_style_circle_background_corner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter" />
+ <solid android:color="@color/pointer_fill_hovered_color" />
+ </shape>
+ </item>
+ <item
+ android:id="@+id/tintableCircleHovered"
+ android:top="@dimen/pointer_fill_style_circle_offset"
+ android:left="@dimen/pointer_fill_style_circle_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_offset"
+ android:right="@dimen/pointer_fill_style_circle_offset">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_diameter"
+ android:height="@dimen/pointer_fill_style_circle_diameter" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_outline_offset"
+ android:left="@dimen/pointer_fill_style_circle_outline_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_outline_offset"
+ android:right="@dimen/pointer_fill_style_circle_outline_offset">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_outline_diameter"
+ android:height="@dimen/pointer_fill_style_circle_outline_diameter" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_outline_stroke"
+ android:color="@android:color/white" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+ <item><!-- default state -->
+ <layer-list>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="@dimen/pointer_fill_style_circle_background_corner_radius" />
+ <size
+ android:width="@dimen/pointer_fill_style_circle_background_outer_diameter"
+ android:height="@dimen/pointer_fill_style_circle_background_outer_diameter" />
+ <solid android:color="@androidprv:color/materialColorSurfaceBright" />
+ </shape>
+ </item>
+ <item
+ android:id="@+id/tintableCircleDefault"
+ android:top="@dimen/pointer_fill_style_circle_offset"
+ android:left="@dimen/pointer_fill_style_circle_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_offset"
+ android:right="@dimen/pointer_fill_style_circle_offset">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_diameter"
+ android:height="@dimen/pointer_fill_style_circle_diameter" />
+ <solid android:color="@android:color/white" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/pointer_fill_style_circle_outline_offset"
+ android:left="@dimen/pointer_fill_style_circle_outline_offset"
+ android:bottom="@dimen/pointer_fill_style_circle_outline_offset"
+ android:right="@dimen/pointer_fill_style_circle_outline_offset">
+ <shape android:shape="oval">
+ <size
+ android:width="@dimen/pointer_fill_style_circle_outline_diameter"
+ android:height="@dimen/pointer_fill_style_circle_outline_diameter" />
+ <stroke
+ android:width="@dimen/pointer_fill_style_circle_outline_stroke"
+ android:color="@android:color/white" />
+ </shape>
+ </item>
+ </layer-list>
+ </item>
+</selector>
diff --git a/res/drawable/pointer_icon_fill_color_foreground.xml b/res/drawable/pointer_icon_fill_color_foreground.xml
deleted file mode 100644
index 3d41620..0000000
--- a/res/drawable/pointer_icon_fill_color_foreground.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true" android:state_hovered="true">
- <layer-list>
- <item>
- <shape android:shape="oval">
- <size android:width="@dimen/pointer_fill_style_circle_hover_selected_diameter"
- android:height="@dimen/pointer_fill_style_circle_hover_selected_diameter" />
- <stroke android:width="@dimen/pointer_fill_style_shape_hovered_stroke"
- android:color="@color/pointer_fill_outline_color" />
- </shape>
- </item>
- <item
- android:top="@dimen/pointer_fill_style_checkmark_hovered_padding"
- android:left="@dimen/pointer_fill_style_checkmark_hovered_padding"
- android:bottom="@dimen/pointer_fill_style_checkmark_hovered_padding"
- android:right="@dimen/pointer_fill_style_checkmark_hovered_padding"
- android:drawable="@drawable/pointer_fill_check_24dp" />
- </layer-list>
- </item>
- <item android:state_selected="true">
- <layer-list>
- <item>
- <inset android:insetTop="@dimen/pointer_fill_style_circle_selected_offset"
- android:insetLeft="@dimen/pointer_fill_style_circle_selected_offset"
- android:insetBottom="@dimen/pointer_fill_style_circle_selected_offset"
- android:insetRight="@dimen/pointer_fill_style_circle_selected_offset">
- <shape android:shape="oval">
- <size android:width="@dimen/pointer_fill_style_circle_selected_diameter"
- android:height="@dimen/pointer_fill_style_circle_selected_diameter" />
- <stroke android:width="@dimen/pointer_fill_style_shape_selected_stroke"
- android:color="@color/pointer_fill_outline_color" />
- </shape>
- </inset>
- </item>
- <item
- android:top="@dimen/pointer_fill_style_checkmark_selected_padding"
- android:left="@dimen/pointer_fill_style_checkmark_selected_padding"
- android:bottom="@dimen/pointer_fill_style_checkmark_selected_padding"
- android:right="@dimen/pointer_fill_style_checkmark_selected_padding"
- android:drawable="@drawable/pointer_fill_check_24dp" />
- </layer-list>
- </item>
- <item android:state_hovered="true">
- <shape android:shape="oval">
- <size android:width="@dimen/pointer_fill_style_circle_hover_diameter"
- android:height="@dimen/pointer_fill_style_circle_hover_diameter" />
- <stroke android:width="@dimen/pointer_fill_style_shape_hovered_stroke"
- android:color="@color/pointer_fill_outline_color" />
- </shape>
- </item>
-</selector>
diff --git a/res/layout/advanced_bt_entity_header.xml b/res/layout/advanced_bt_entity_header.xml
index 37ae843..83657b1 100644
--- a/res/layout/advanced_bt_entity_header.xml
+++ b/res/layout/advanced_bt_entity_header.xml
@@ -76,6 +76,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
+ android:layoutDirection="ltr"
android:orientation="horizontal">
<include
diff --git a/res/layout/advanced_bt_entity_sub.xml b/res/layout/advanced_bt_entity_sub.xml
index 252ab47..77bc4f0 100644
--- a/res/layout/advanced_bt_entity_sub.xml
+++ b/res/layout/advanced_bt_entity_sub.xml
@@ -21,6 +21,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:layoutDirection="locale"
android:focusable="true"
android:orientation="vertical">
diff --git a/res/layout/apn_preference_layout.xml b/res/layout/apn_preference_layout.xml
deleted file mode 100644
index 9b6efe7..0000000
--- a/res/layout/apn_preference_layout.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:focusable="false"
- android:gravity="center_vertical">
-
- <RelativeLayout
- android:id="@+id/text_layout"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:layout_weight="1"
- android:focusable="true"
- android:clickable="true"
- android:background="?android:attr/selectableItemBackground">
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:focusable="false"
- android:labelFor="@id/apn_radio_button_frame"
- android:singleLine="true"
- android:textAppearance="?android:attr/textAppearanceListItem" />
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignStart="@android:id/title"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary"
- android:textColor="?android:attr/textColorSecondary"
- android:focusable="false"
- android:hyphenationFrequency="normalFast"
- android:lineBreakWordStyle="phrase"
- android:maxLines="2" />
-
- </RelativeLayout>
-
- <FrameLayout
- android:id="@+id/apn_radio_button_frame"
- android:layout_width="@dimen/min_tap_target_size"
- android:layout_height="@dimen/min_tap_target_size"
- android:layout_margin="8dp">
-
- <RadioButton
- android:id="@+id/apn_radiobutton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:clickable="false"
- android:focusable="false" />
- </FrameLayout>
-
-</LinearLayout>
diff --git a/res/layout/battery_chart_graph.xml b/res/layout/battery_chart_graph.xml
index 9e816ed..358cdd0 100644
--- a/res/layout/battery_chart_graph.xml
+++ b/res/layout/battery_chart_graph.xml
@@ -36,8 +36,7 @@
android:id="@+id/battery_chart_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:alpha="0">
+ android:orientation="vertical">
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
android:id="@+id/daily_battery_chart"
android:layout_width="match_parent"
@@ -53,7 +52,8 @@
android:layout_width="match_parent"
android:layout_height="@dimen/chartview_layout_height"
android:layout_marginBottom="16dp"
- android:visibility="visible"
+ android:visibility="gone"
+ android:alpha="0"
android:contentDescription="@string/hourly_battery_usage_chart"
android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary" />
diff --git a/res/layout/display_topology_preference.xml b/res/layout/display_topology_preference.xml
index d2e4300..beaf816 100644
--- a/res/layout/display_topology_preference.xml
+++ b/res/layout/display_topology_preference.xml
@@ -16,8 +16,9 @@
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/display_topology_pane_holder"
android:importantForAccessibility="no"
- android:layout_height="160dp"
+ android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingHorizontal="@dimen/display_topology_pane_margin"
android:orientation="horizontal">
@@ -27,14 +28,14 @@
android:layout_width="match_parent"
android:src="@drawable/display_topology_background"/>
<FrameLayout
- android:id="@+id/display_topology_container"
+ android:id="@+id/display_topology_pane_content"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="match_parent"/>
<TextView
android:id="@+id/topology_hint"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="top|center_horizontal"
- android:paddingTop="10dp"
- android:text="@string/external_display_topology_hint"/>
+ android:paddingBottom="10dp"
+ android:paddingTop="10dp" />
</FrameLayout>
diff --git a/res/layout/keyboard_layout_dialog_switch_hint.xml b/res/layout/keyboard_layout_dialog_switch_hint.xml
deleted file mode 100644
index af8c3f4..0000000
--- a/res/layout/keyboard_layout_dialog_switch_hint.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <ImageView android:id="@+id/titleDivider"
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:scaleType="fitXY"
- android:gravity="fill_horizontal"
- android:src="@android:drawable/divider_horizontal_dark"
- android:contentDescription="@null" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/keyboard_layout_dialog_switch_hint"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorAlertDialogListItem"
- android:minHeight="48dp"
- android:gravity="center" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/pointer_icon_fill_style_layout.xml b/res/layout/pointer_icon_fill_style_layout.xml
index aba57a6..2be750f 100644
--- a/res/layout/pointer_icon_fill_style_layout.xml
+++ b/res/layout/pointer_icon_fill_style_layout.xml
@@ -42,7 +42,6 @@
android:layout_height="@dimen/pointer_fill_container_height"
android:layout_marginBottom="@dimen/pointer_fill_style_circle_padding"
android:layout_marginTop="@dimen/pointer_fill_style_circle_padding"
- android:background="@drawable/pointer_icon_fill_container_background"
android:gravity="center"
android:paddingTop="@dimen/pointer_fill_style_container_padding"
android:paddingBottom="@dimen/pointer_fill_style_container_padding">
@@ -53,19 +52,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="4" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_black"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_black_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -73,19 +71,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="3" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_green"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_green_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -93,19 +90,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="3" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_red"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_red_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -113,19 +109,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="3" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_pink"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_pink_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -133,19 +128,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="3" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_blue"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_blue_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -153,19 +147,18 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="3" />
+ android:layout_weight="1" />
<ImageView
android:id="@+id/button_purple"
- android:layout_width="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_width="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:layout_weight="0"
- android:layout_height="@dimen/pointer_fill_style_circle_diameter"
+ android:layout_height="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:adjustViewBounds="true"
- android:maxWidth="@dimen/pointer_fill_style_circle_diameter"
+ android:maxWidth="@dimen/pointer_fill_style_circle_background_outer_diameter"
android:contentDescription="@string/pointer_fill_style_purple_button"
android:scaleType="center"
- android:background="@drawable/pointer_icon_fill_color_background"
- android:src="@drawable/pointer_icon_fill_color_foreground" />
+ android:src="@drawable/pointer_icon_fill_color_background" />
<View
android:layout_width="0dp"
@@ -173,7 +166,7 @@
android:focusable="false"
android:clickable="false"
android:importantForAccessibility="no"
- android:layout_weight="4" />
+ android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/settings_main_prefs.xml b/res/layout/settings_main_prefs.xml
index 48352e2..affdd7a 100644
--- a/res/layout/settings_main_prefs.xml
+++ b/res/layout/settings_main_prefs.xml
@@ -28,6 +28,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
+ <com.android.settings.widget.SettingsMainSwitchBar
+ android:id="@+id/expressive_switch_bar"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dip"/>
+
<FrameLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
diff --git a/res/raw/lottie_bubbles.json b/res/raw/lottie_bubbles.json
index e69de29..b0e5993 100644
--- a/res/raw/lottie_bubbles.json
+++ b/res/raw/lottie_bubbles.json
@@ -0,0 +1 @@
+{"v":"5.7.8","fr":60,"ip":0,"op":406,"w":412,"h":300,"nm":"Bubbles_EDU","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[165.599,75.148,0],"ix":2,"l":2},"a":{"a":0,"k":[4,4,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.693],[1.693,0],[0,1.693],[-1.694,0]],"o":[[0,1.693],[-1.694,0],[0,-1.693],[1.693,0]],"v":[[3.066,0],[0.001,3.065],[-3.066,0],[0.001,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.715,4.306],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":381,"st":-219,"bm":0}]},{"id":"comp_1","layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[261.163,141.906,0],"ix":2,"l":2},"a":{"a":0,"k":[4,4,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.693],[1.693,0],[0,1.693],[-1.694,0]],"o":[[0,1.693],[-1.694,0],[0,-1.693],[1.693,0]],"v":[[3.066,0],[0.001,3.065],[-3.066,0],[0.001,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.715,4.306],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":239,"st":20,"bm":0}]},{"id":"comp_2","layers":[{"ddd":0,"ind":1,"ty":4,"nm":".red400","cl":"red400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[184.011,75.101,0],"ix":2,"l":2},"a":{"a":0,"k":[3.5,3.5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":158,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":180,"s":[90,90,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[1,1,0.8],"y":[0,0,0]},"t":275,"s":[90,90,100]},{"t":297,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.693],[1.693,0],[0,1.693],[-1.694,0]],"o":[[0,1.693],[-1.694,0],[0,-1.693],[1.693,0]],"v":[[3.066,0],[0.001,3.065],[-3.066,0],[0.001,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.933333333333,0.403921568627,0.360784313725,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.464,3.623],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0}]},{"id":"comp_3","layers":[{"ddd":0,"ind":1,"ty":4,"nm":".yellow400","cl":"yellow400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[203.168,75.101,0],"ix":2,"l":2},"a":{"a":0,"k":[3.5,3.5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":160,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":182,"s":[90,90,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[1,1,0.8],"y":[0,0,0]},"t":273,"s":[90,90,100]},{"t":295,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.693],[1.693,0],[0,1.693],[-1.694,0]],"o":[[0,1.693],[-1.694,0],[0,-1.693],[1.693,0]],"v":[[3.066,0],[0.001,3.065],[-3.066,0],[0.001,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.788235294118,0.203921568627,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.643,3.623],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0}]},{"id":"comp_4","layers":[{"ddd":0,"ind":1,"ty":4,"nm":".green400","cl":"green400","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[221.926,75.104,0],"ix":2,"l":2},"a":{"a":0,"k":[4,3.5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":162,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":184,"s":[90,90,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[1,1,0.8],"y":[0,0,0]},"t":271,"s":[90,90,100]},{"t":293,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.004,-1.693],[1.693,-0.005],[0.005,1.693],[-1.694,0.004]],"o":[[0.005,1.693],[-1.693,0.004],[-0.004,-1.693],[1.693,-0.005]],"v":[[3.065,-0.008],[0.008,3.066],[-3.066,0.008],[-0.008,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.356862745098,0.725490196078,0.454901960784,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.725,3.634],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Touch 4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":254,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":272,"s":[59]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":273,"s":[59]},{"t":288,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[226.55,48.3,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[9,9,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":254,"s":[200,200]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":272,"s":[150,150]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":273,"s":[150,150]},{"t":288,"s":[200,200]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":1000,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803926945,0.0941176489,0.537254929543,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":12,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.983088254929,0.665331482887,0.832399487495,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":254,"op":288,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Touch 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":122,"s":[0]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":133,"s":[59]},{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":134,"s":[59]},{"t":149,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[252.3,140.8,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[9,9,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":122,"s":[200,200]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":133,"s":[150,150]},{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.4,0.4],"y":[0,0]},"t":134,"s":[150,150]},{"t":149,"s":[200,200]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":1000,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":1,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.909803926945,0.0941176489,0.537254929543,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":12,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.983088254929,0.665331482887,0.832399487495,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":122,"op":149,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".grey300","cl":"grey300","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":156,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300,"s":[0]},{"t":312,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[205.95,154.75,0],"ix":2,"l":2},"a":{"a":0,"k":[67,1,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[0.832,1.105],[133.152,1.105]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".grey300","cl":"grey300","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":156,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300,"s":[0]},{"t":312,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,213.25,0],"ix":2,"l":2},"a":{"a":0,"k":[36,49,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.199],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.199]],"v":[[4.009,8.009],[-4.009,8.009],[-8.009,4.009],[-8.009,-4.009],[-4.009,-8.009],[4.009,-8.009],[8.009,-4.009],[8.009,4.009]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[63.647,57.865],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[63.647,33.26],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[63.647,8.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.199,0],[0,0],[0,2.199],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.199,0],[0,0],[0,2.199]],"v":[[4.009,8.009],[-4.009,8.009],[-8.009,4.009],[-8.009,-4.009],[-4.009,-8.009],[4.009,-8.009],[8.009,-4.009],[8.009,4.009]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[35.658,57.865],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.199,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.199,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[35.658,33.26],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.199,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.199,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[35.658,8.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.199],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.199]],"v":[[4.009,8.009],[-4.009,8.009],[-8.009,4.009],[-8.009,-4.009],[-4.009,-8.009],[4.009,-8.009],[8.009,-4.009],[8.009,4.009]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.353,57.865],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.353,33.26],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 8","np":2,"cix":2,"bm":0,"ix":8,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.2,0],[0,0],[0,2.2],[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0]],"o":[[0,0],[-2.2,0],[0,0],[0,-2.2],[0,0],[2.2,0],[0,0],[0,2.2]],"v":[[4.009,8.008],[-4.009,8.008],[-8.009,4.008],[-8.009,-4.008],[-4.009,-8.008],[4.009,-8.008],[8.009,-4.008],[8.009,4.008]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.353,8.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 9","np":2,"cix":2,"bm":0,"ix":9,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.694,0],[0,4.694],[-4.694,0]],"o":[[0,4.694],[-4.694,0],[0,-4.694],[4.694,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[36,88.481],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 10","np":2,"cix":2,"bm":0,"ix":10,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".grey300","cl":"grey300","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":156,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300,"s":[0]},{"t":312,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,264.1,0],"ix":2,"l":2},"a":{"a":0,"k":[16,1.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.34,0],[0,0],[0,-0.34],[-0.34,0],[0,0],[0,0.34]],"o":[[0,0],[-0.34,0],[0,0.34],[0,0],[0.34,0],[0,-0.34]],"v":[[14.795,-0.618],[-14.795,-0.618],[-15.413,0],[-14.795,0.618],[14.795,0.618],[15.413,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16,1.309],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".grey300","cl":"grey300","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,264.1,0],"ix":2,"l":2},"a":{"a":0,"k":[16,1.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.34,0],[0,0],[0,-0.34],[-0.34,0],[0,0],[0,0.34]],"o":[[0,0],[-0.34,0],[0,0.34],[0,0],[0.34,0],[0,-0.34]],"v":[[14.795,-0.618],[-14.795,-0.618],[-15.413,0],[-14.795,0.618],[14.795,0.618],[15.413,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854902020623,0.862745157878,0.878431432387,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16,1.309],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"ChatMatte","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Top Ui Notification Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":171,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":286.428,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[205.95,94,0],"ix":2,"l":2},"a":{"a":0,"k":[64,10.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.866],[3.866,0],[0,3.866],[-3.866,0]],"o":[[0,3.866],[-3.866,0],[0,-3.866],[3.866,0]],"v":[[7,0],[0,7],[-7,0],[0,-7]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.922,7.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[1.284,19.79],[126.691,19.79]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.947865325329,0.955448584463,0.959243056353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.914,0],[0,0],[0,0.913],[0,0],[0.914,0],[0,0],[0,-0.939],[0,0]],"o":[[0,0],[0.914,0],[0,0],[0,-0.914],[0,0],[-0.94,0],[0,0],[0,0.913]],"v":[[-6.199,1.684],[6.199,1.684],[7.861,0.023],[7.861,-0.023],[6.199,-1.684],[-6.153,-1.684],[-7.861,0.023],[-7.861,0.023]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.914689307119,0.922272566253,0.932703414618,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[32.414,10.243],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.909,0],[0,0],[0,0.909],[0,0],[0.91,0],[0,0],[0,-0.944]],"o":[[0,0],[0.91,0],[0,0],[0,-0.909],[0,0],[-0.944,0],[0,0.909]],"v":[[-12.824,1.684],[12.824,1.684],[14.478,0.031],[14.478,-0.031],[12.824,-1.684],[-12.762,-1.684],[-14.478,0.031]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.914689307119,0.922272566253,0.932703414618,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.031,5.695],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.457],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.457]],"v":[[0,4.449],[0,4.449],[-4.448,-0.001],[0,-4.449],[4.448,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[95.312,7.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":2,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.457],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.457]],"v":[[0,4.449],[0,4.449],[-4.448,-0.001],[0,-4.449],[4.448,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[106.902,7.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 6","np":2,"cix":2,"bm":0,"ix":6,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.457],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.457]],"v":[[0.001,4.449],[0.001,4.449],[-4.449,-0.001],[0.001,-4.449],[4.449,-0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[118.492,7.969],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 7","np":2,"cix":2,"bm":0,"ix":7,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"ChatMatte 2","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Chat 01 Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":174,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":283,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":188.85,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.001],"y":[0]},"t":160,"s":[145.75]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":181,"s":[155.2]},{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":275,"s":[155.2]},{"t":299,"s":[145.75]}],"ix":4}},"a":{"a":0,"k":[39,8.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.078,0],[0,0],[0,4.078],[4.078,0],[0,0],[0,-4.077]],"o":[[0,0],[4.078,0],[0,-4.077],[0,0],[-4.078,0],[0,4.078]],"v":[[-30.765,7.414],[30.765,7.414],[38.179,0],[30.765,-7.414],[-30.765,-7.414],[-38.179,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.914689307119,0.922272566253,0.932703414618,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.617,8.627],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"ChatMatte 3","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"Chat 02 Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":176,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280.715,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":223.05,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.001],"y":[0]},"t":160,"s":[162.4]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":181,"s":[171.85]},{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":275,"s":[171.85]},{"t":299,"s":[162.4]}],"ix":4}},"a":{"a":0,"k":[39,8,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[4.077,0],[0,0],[0,4.078],[-4.077,0],[0,0],[0,-4.077]],"o":[[0,0],[-4.077,0],[0,-4.077],[0,0],[4.077,0],[0,4.078]],"v":[[30.766,7.414],[-30.765,7.414],[-38.179,0],[-30.765,-7.414],[30.766,-7.414],[38.179,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803981407,0.941176530427,0.996078491211,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.797,8.291],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"ChatMatte 4","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"Chat 03 Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":178,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":278.428,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":223.05,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.001],"y":[0]},"t":160,"s":[182.65]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":181,"s":[192.1]},{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":275,"s":[192.1]},{"t":299,"s":[182.65]}],"ix":4}},"a":{"a":0,"k":[39,11.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[5.904,0],[0,0],[0,5.904],[-5.904,0],[0,0],[0,-5.905]],"o":[[0,0],[-5.904,0],[0,-5.905],[0,0],[5.905,0],[0,5.904]],"v":[[27.445,10.736],[-27.444,10.736],[-38.179,0.001],[-27.444,-10.736],[27.444,-10.736],[38.179,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803981407,0.941176530427,0.996078491211,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.797,11.276],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"ChatMatte 5","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":"Chat 04 Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":180,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":276.143,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":188.85,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.001],"y":[0]},"t":160,"s":[202]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":181,"s":[211.45]},{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":275,"s":[211.45]},{"t":299,"s":[202]}],"ix":4}},"a":{"a":0,"k":[39,8,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-4.078,0],[0,0],[0,4.078],[4.078,0],[0,0],[0,-4.078]],"o":[[0,0],[4.078,0],[0,-4.078],[0,0],[-4.078,0],[0,4.078]],"v":[[-30.765,7.414],[30.765,7.414],[38.179,0],[30.765,-7.414],[-30.765,-7.414],[-38.179,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.914689307119,0.922272566253,0.932703414618,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[38.617,8.262],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"ChatMatte 6","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":"Chat 05 Outlines","tt":1,"sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":181,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":275,"s":[100]},{"t":299,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":0,"k":205.95,"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.001],"y":[0]},"t":160,"s":[227.65]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":181,"s":[237.1]},{"i":{"x":[0.999],"y":[1]},"o":{"x":[0.8],"y":[0]},"t":275,"s":[237.1]},{"t":299,"s":[227.65]}],"ix":4}},"a":{"a":0,"k":[58,8.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.456],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.456]],"v":[[0,4.449],[0,4.449],[-4.448,0.001],[0,-4.449],[4.448,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[4.892,8.624],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.456],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.456]],"v":[[0,4.449],[0,4.449],[-4.448,0.001],[0,-4.449],[4.448,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.481,8.624],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.457,0],[0,0],[0,2.456],[-2.457,0],[0,-2.457]],"o":[[0,0],[-2.457,0],[0,-2.457],[2.457,0],[0,2.456]],"v":[[0.001,4.449],[0.001,4.449],[-4.449,0.001],[0.001,-4.449],[4.449,0.001]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.071,8.624],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-3.79,0],[0,0],[0,3.79],[0,0],[3.79,0],[0,0],[0,-4.034],[0,0]],"o":[[0,0],[3.79,0],[0,0],[0,-3.791],[0,0],[-4.033,0],[0,0],[0,3.79]],"v":[[-33.09,7.414],[33.092,7.414],[39.982,0.522],[39.982,-0.523],[33.092,-7.414],[-32.648,-7.414],[-39.982,-0.08],[-39.982,0.522]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.914689307119,0.922272566253,0.932703414618,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[74.994,8.624],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"Chat Pointer Outlines","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":166,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":292.143,"s":[100]},{"t":293.28515625,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":165,"s":[158.7,84.325,0],"to":[0,-0.412,0],"ti":[0,0.412,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":168,"s":[158.7,81.85,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":289.857,"s":[158.7,81.85,0],"to":[0,0.413,0],"ti":[0,-0.413,0]},{"t":293.28515625,"s":[158.7,84.325,0]}],"ix":2,"l":2},"a":{"a":0,"k":[3.5,3,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-2.786,1.115],[-0.001,-1.115],[2.786,1.115]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.874,1.475],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":616,"st":16,"bm":0},{"ddd":0,"ind":20,"ty":4,"nm":"ChatMatte 7","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[24]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":165,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":293.285,"s":[100]},{"t":299,"s":[24]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,166.75,0],"ix":2,"l":2},"a":{"a":0,"k":[-0.575,-36.689,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.001,"y":0},"t":160,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.833,"y":1},"o":{"x":0.167,"y":0},"t":181,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"i":{"x":0.999,"y":1},"o":{"x":0.8,"y":0},"t":275,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.5,83.478],[53.5,86.478],[-53.5,86.478],[-56.5,83.478],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]},{"t":299,"s":[{"i":[[0,-1.657],[0,0],[1.657,0],[0,0],[0,1.657],[0,0],[-1.657,0],[0,0]],"o":[[0,0],[0,1.657],[0,0],[-1.657,0],[0,0],[0,-1.657],[0,0],[1.657,0]],"v":[[56.5,-83.478],[56.575,-89.522],[53.575,-86.522],[-53.425,-86.522],[-56.425,-89.522],[-56.5,-83.478],[-53.5,-86.478],[53.5,-86.478]],"c":true}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-0.575,-36.689],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":160,"op":600,"st":0,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":".blue300","cl":"blue300","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[100]},{"t":154,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[244.662,135.587,0],"ix":2,"l":2},"a":{"a":0,"k":[38,8.125,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.001,0.001,0.001],"y":[0,0,0]},"t":45,"s":[0,0,100]},{"i":{"x":[0.71,0.71,0.833],"y":[1,1,1]},"o":{"x":[0.318,0.318,0.001],"y":[0,0,0]},"t":59,"s":[94.5,94.5,100]},{"t":68,"s":[90,90,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[1.244,0],[0,0],[0,-1.244],[0,0],[-1.244,0],[0,0],[0,1.244],[0,0]],"o":[[0,0],[0,0],[0,-1.244],[0,0],[-1.244,0],[0,0],[0,1.244],[0,0],[1.244,0],[0,0],[0,0]],"v":[[18.328,0],[17.101,-1.227],[17.101,-4.883],[14.84,-7.145],[-16.067,-7.145],[-18.328,-4.883],[-18.328,4.883],[-16.067,7.145],[14.84,7.145],[17.101,4.883],[17.101,1.227]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.541176470588,0.705882352941,0.972549019608,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[19.046,8.106],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":22,"ty":4,"nm":".grey300","cl":"grey300","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.243,0],[0,0],[0,5.244],[0,0],[-5.243,0],[0,0],[0,-5.244]],"o":[[0,5.244],[0,0],[-5.243,0],[0,0],[0,-5.244],[0,0],[5.243,0],[0,0]],"v":[[64.188,114.112],[54.679,123.622],[-54.679,123.622],[-64.188,114.112],[-64.188,-114.112],[-54.679,-123.622],[54.679,-123.622],[64.188,-114.112]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,1.313],[0,0],[1.313,0],[0,0],[6.554,0],[0,0],[0,-6.555],[0,0],[-6.554,0],[0,0],[0,6.555],[0,0],[0,1.313],[0,0],[1.313,0],[0,0]],"o":[[0,0],[0,-1.313],[0,0],[0,-6.555],[0,0],[-6.554,0],[0,0],[0,6.555],[0,0],[6.554,0],[0,0],[1.313,0],[0,0],[0,-1.313],[0,0],[1.313,0]],"v":[[68.943,-52.302],[68.943,-61.811],[66.566,-64.188],[66.566,-114.112],[54.679,-126],[-54.679,-126],[-66.566,-114.112],[-66.566,114.112],[-54.679,126],[54.679,126],[66.566,114.112],[66.566,-2.378],[68.943,-4.755],[68.943,-28.528],[66.566,-30.906],[66.566,-49.924]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.854901969433,0.86274510622,0.878431379795,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1800,"st":0,"bm":0},{"ddd":0,"ind":23,"ty":0,"nm":"BlueDot","parent":24,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[62.89,101.169,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[111.111,111.111,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":239,"op":620,"st":239,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".blue50","cl":"blue50","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"s":true,"x":{"a":1,"k":[{"i":{"x":[0.437],"y":[1]},"o":{"x":[0.637],"y":[0]},"t":294,"s":[159.299]},{"t":324,"s":[254.963]}],"ix":3},"y":{"a":1,"k":[{"i":{"x":[0.152],"y":[1]},"o":{"x":[0.414],"y":[0]},"t":306,"s":[68.848]},{"t":350,"s":[135.585]}],"ix":4}},"a":{"a":0,"k":[11,11,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.694,0],[0,4.694],[-4.694,0]],"o":[[0,4.694],[-4.694,0],[0,-4.694],[4.694,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11,11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":239,"op":600,"st":0,"bm":0},{"ddd":0,"ind":25,"ty":0,"nm":"BlueDot","parent":26,"refId":"comp_1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-43.429,27.058,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[111.111,111.111,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":239,"st":0,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".blue50","cl":"blue50","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[254.963,135.566,0],"to":[0.004,-63.556,0],"ti":[94.364,1.013,0]},{"t":168,"s":[159.299,68.86,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":288.551,"s":[158.486,68.672,0],"to":[82.114,1.013,0],"ti":[-0.996,-58.556,0]},{"t":332,"s":[254.65,134.91,0]}],"ix":2,"l":2},"a":{"a":0,"k":[11,11,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.694,0],[0,4.694],[-4.694,0]],"o":[[0,4.694],[-4.694,0],[0,-4.694],[4.694,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.4,0.61568627451,0.964705882353,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[11,11],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":239,"st":0,"bm":0},{"ddd":0,"ind":27,"ty":0,"nm":"RedDot","parent":28,"refId":"comp_2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[40.056,99.722,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[111.111,111.111,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":28,"ty":4,"nm":".red50","cl":"red50","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":144,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":155.854,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":302,"s":[100]},{"t":315,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[254.775,135.175,0],"to":[-0.412,-66.188,0],"ti":[73.312,0.488,0]},{"t":169,"s":[178.05,68.8,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":287.242,"s":[178.05,68.8,0],"to":[73.312,0.488,0],"ti":[-0.413,-66.188,0]},{"t":329,"s":[254.775,135.175,0]}],"ix":2,"l":2},"a":{"a":0,"k":[9,9.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.695,0],[0,4.695],[-4.694,0]],"o":[[0,4.695],[-4.694,0],[0,-4.694],[4.695,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.988235294118,0.909803921569,0.901960784314,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.054,9.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":29,"ty":0,"nm":"YellowDot","parent":30,"refId":"comp_3","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[19.056,99.722,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[111.111,111.111,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":30,"ty":4,"nm":".yellow50","cl":"yellow50","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":146.734,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":158.588,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":300.885,"s":[100]},{"t":314,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[254.55,134.95,0],"to":[0.3,-65.925,0],"ti":[57.75,0.225,0]},{"t":170,"s":[196.95,68.8,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":285.93,"s":[196.95,68.8,0],"to":[57.75,0.225,0],"ti":[0.3,-65.925,0]},{"t":327,"s":[254.55,134.95,0]}],"ix":2,"l":2},"a":{"a":0,"k":[9,9.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.694,0],[0,4.695],[-4.694,0]],"o":[[0,4.695],[-4.694,0],[0,-4.694],[4.694,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.996078431373,0.96862745098,0.878431372549,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.234,9.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":31,"ty":0,"nm":"GreenDot","refId":"comp_4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"w":412,"h":300,"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":32,"ty":4,"nm":".green50","cl":"green50","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":149.471,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":161.324,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":297.301,"s":[100]},{"t":312,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[255,134.95,0],"to":[-0.3,-65.925,0],"ti":[39.225,0.225,0]},{"t":171,"s":[215.4,68.8,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":284.621,"s":[215.4,68.8,0],"to":[39.225,0.225,0],"ti":[-0.3,-65.925,0]},{"t":325,"s":[255,134.95,0]}],"ix":2,"l":2},"a":{"a":0,"k":[9.5,9.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.013,-4.694],[4.695,-0.012],[0.013,4.694],[-4.695,0.013]],"o":[[0.013,4.694],[-4.694,0.013],[-0.013,-4.694],[4.694,-0.012]],"v":[[8.5,-0.023],[0.022,8.499],[-8.5,0.023],[-0.022,-8.501]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.901960784314,0.956862745098,0.917647058824,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.297,9.684],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":33,"ty":4,"nm":"BlueDot","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[240.657,75.109,0],"ix":2,"l":2},"a":{"a":0,"k":[3.5,3.5,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0,0,0.2],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":164,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":186,"s":[90,90,100]},{"i":{"x":[0.6,0.6,0.6],"y":[1,1,1]},"o":{"x":[1,1,0.8],"y":[0,0,0]},"t":269,"s":[90,90,100]},{"t":291,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.693],[1.693,0],[0,1.693],[-1.694,0]],"o":[[0,1.693],[-1.694,0],[0,-1.693],[1.693,0]],"v":[[3.066,0],[0.001,3.065],[-3.066,0],[0.001,-3.065]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.297618701411,0.5469255036,0.959243056353,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[3.331,3.623],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":34,"ty":4,"nm":"BlueBubble","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":152.205,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":164.059,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":293.715,"s":[100]},{"t":309.25,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[254.775,134.725,0],"to":[-0.15,-65.475,0],"ti":[20.362,0.188,0]},{"t":172,"s":[233.85,68.8,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":283.311,"s":[233.85,68.8,0],"to":[20.362,0.188,0],"ti":[-0.15,-65.475,0]},{"t":325,"s":[254.775,134.725,0]}],"ix":2,"l":2},"a":{"a":0,"k":[9,9.5,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-4.694],[4.694,0],[0,4.695],[-4.694,0]],"o":[[0,4.695],[-4.694,0],[0,-4.694],[4.694,0]],"v":[[8.5,0],[0,8.5],[-8.5,0],[0,-8.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.909803921569,0.941176470588,0.996078431373,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.922,9.655],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0},{"ddd":0,"ind":35,"ty":4,"nm":"+","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":155,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":167,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":283,"s":[100]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.4,"y":0},"t":144,"s":[254.668,134.855,0],"to":[-0.387,-11.006,0],"ti":[2.531,0.405,0]},{"t":173,"s":[252.345,68.82,0],"h":1},{"i":{"x":0.6,"y":1},"o":{"x":0.8,"y":0},"t":282,"s":[252.345,68.82,0],"to":[2.531,0.405,0],"ti":[-0.304,-9.922,0]},{"t":325,"s":[254.668,134.855,0]}],"ix":2,"l":2},"a":{"a":0,"k":[9,9,0],"ix":1,"l":2},"s":{"a":0,"k":[90,90,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[3,0.5],[-3,0.5],[-3,-0.5],[3,-0.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.204000001795,0.455000005984,0.877999997606,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.797,9.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[0.5,3],[-0.5,3],[-0.5,-3],[0.5,-3]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.204000001795,0.455000005984,0.877999997606,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.797,9.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.013,-4.694],[4.695,-0.012],[0.013,4.695],[-4.695,0.013]],"o":[[0.013,4.694],[-4.694,0.013],[-0.012,-4.694],[4.694,-0.012]],"v":[[8.5,-0.023],[0.021,8.499],[-8.5,0.023],[-0.022,-8.501]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.797,9.064],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":36,"ty":4,"nm":".grey600","cl":"grey600","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":156,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":172,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":284,"s":[100]},{"t":316,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[206,150,0],"ix":2,"l":2},"a":{"a":0,"k":[67,133,0],"ix":1,"l":2},"s":{"a":0,"k":[96.6,94,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.77,0],[0,0],[0,2.769],[0,0],[-5.334,0.268],[0,0],[0,-2.769],[0,0]],"o":[[0,0],[-2.769,0],[0,0],[0.09,-4.634],[0,0],[2.77,0],[0,0],[0,2.769]],"v":[[58.38,132.51],[-59.674,132.51],[-67.363,124.149],[-67.363,-122.821],[-58.315,-132.51],[58.38,-132.51],[67.298,-126.81],[67.298,124.149]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[66.985,133.061],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":620,"st":20,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
index a117e47..3892138 100644
--- a/res/values-night/colors.xml
+++ b/res/values-night/colors.xml
@@ -77,8 +77,8 @@
<!-- Switch bar disabled state color-->
<color name="switch_bar_state_disabled_color">#1FE3E3E3</color>
- <!-- Pointer fill color setting outline color-->
- <color name="pointer_fill_outline_color">#FFFFFF</color>
+ <!-- Pointer fill color setting, fill selector's background color on hover. -->
+ <color name="pointer_fill_hovered_color">#3E373C</color>
<!-- Connected displays -->
<color name="display_topology_background_color">@color/settingslib_color_charcoal</color>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index febdb04..5de6222 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -1425,6 +1425,14 @@
<item>sat</item>
</string-array>
+ <!-- A list for measurement system. [DO NOT TRANSLATE] -->
+ <string-array name="measurement_system">
+ <item>default</item>
+ <item>metric</item>
+ <item>ussystem</item>
+ <item>uksystem</item>
+ </string-array>
+
<!-- Screen flash notification color when activating -->
<array name="screen_flash_notification_preset_opacity_colors">
<item>@color/screen_flash_preset_opacity_color_01</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 200253a..87c3ce9 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -50,8 +50,6 @@
<attr name="android:textAppearance" />
</declare-styleable>
- <attr name="apnPreferenceStyle" format="reference" />
-
<attr name="slicePreferenceStyle" format="reference" />
<attr name="cardPreferenceStyle" format="reference" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0b7e0c1..6e3f96f 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -219,8 +219,8 @@
<!-- Switch bar disabled state color-->
<color name="switch_bar_state_disabled_color">#1F1F1F1F</color>
- <!-- Pointer fill color setting outline color-->
- <color name="pointer_fill_outline_color">#000000</color>
+ <!-- Pointer fill color setting, fill selector's background color on hover. -->
+ <color name="pointer_fill_hovered_color">#1C201A1E</color>
<!-- Connected displays -->
<color name="display_topology_background_color">@color/settingslib_color_grey100</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d202f85..2784f37 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -180,21 +180,28 @@
<dimen name="keyboard_picker_text_size">16sp</dimen>
<!-- Pointer -->
- <dimen name="pointer_fill_container_height">88dp</dimen>
- <dimen name="pointer_fill_container_max_width">448dp</dimen>
+ <dimen name="pointer_fill_container_height">80dp</dimen>
+ <dimen name="pointer_fill_container_max_width">468dp</dimen>
<dimen name="pointer_fill_style_container_padding">32dp</dimen>
- <dimen name="pointer_fill_style_circle_diameter">56dp</dimen>
- <dimen name="pointer_fill_style_circle_hover_selected_diameter">52dp</dimen>
- <dimen name="pointer_fill_style_circle_hover_diameter">56dp</dimen>
- <dimen name="pointer_fill_style_circle_selected_diameter">50dp</dimen>
- <dimen name="pointer_fill_style_circle_inner_diameter">40dp</dimen>
- <dimen name="pointer_fill_style_circle_offset">8dp</dimen>
+ <dimen name="pointer_fill_style_target_width">64dp</dimen>
+ <dimen name="pointer_fill_style_circle_diameter">39dp</dimen>
+ <dimen name="pointer_fill_style_circle_outline_diameter">39.5dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_corner_radius">20dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_corner_inner_radius">16dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_outer_diameter">64dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_outer_diameter_less_stroke">62dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_outer_diameter_selected">52dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_outline_offset">1dp</dimen>
+ <dimen name="pointer_fill_style_circle_offset">12.5dp</dimen>
+ <dimen name="pointer_fill_style_circle_offset_selected">13.25dp</dimen>
+ <dimen name="pointer_fill_style_circle_outline_offset">12.25dp</dimen>
+ <dimen name="pointer_fill_style_circle_outline_offset_selected">13dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_offset">7dp</dimen>
+ <dimen name="pointer_fill_style_circle_outline_stroke">1dp</dimen>
+ <dimen name="pointer_fill_style_circle_background_selected_outline_stroke">2dp</dimen>
<dimen name="pointer_fill_style_circle_padding">16dp</dimen>
- <dimen name="pointer_fill_style_circle_selected_offset">3dp</dimen>
- <dimen name="pointer_fill_style_checkmark_selected_padding">14dp</dimen>
- <dimen name="pointer_fill_style_checkmark_hovered_padding">17dp</dimen>
- <dimen name="pointer_fill_style_shape_selected_stroke">2dp</dimen>
- <dimen name="pointer_fill_style_shape_hovered_stroke">4dp</dimen>
+ <dimen name="pointer_fill_style_checkmark_selected_padding">16dp</dimen>
+ <dimen name="pointer_fill_style_checkmark_size">24dp</dimen>
<dimen name="pointer_stroke_style_padding">8dp</dimen>
<dimen name="pointer_stroke_style_text_padding">21dp</dimen>
<dimen name="pointer_scale_padding">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3300cc6..8e5e04f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -506,6 +506,8 @@
<string name="first_day_of_week_preferences_title">First day of week</string>
<!-- The title of the menu entry of Numbers system preference. [CHAR LIMIT=50] -->
<string name="numbers_preferences_title">Numbers preferences</string>
+ <!-- The title of the menu entry of Measurement system preference. [CHAR LIMIT=50] -->
+ <string name="measurement_system_preferences_title">Measurement system</string>
<!-- The summary of default string for each regional preference. [CHAR LIMIT=50] -->
<string name="default_string_of_regional_preference">Use default</string>
<!-- The title of Celsius for preference of temperature unit. [CHAR LIMIT=50] -->
@@ -532,6 +534,12 @@
<string name="desc_regional_pref_footer_learn_more">Learn more about language preferences.</string>
<!-- Support link for the regional preference page. [CHAR LIMIT=NONE]-->
<string name="regional_pref_footer_learn_more_link" translatable="false">https://support.google.com/android?p=regional_preferences</string>
+ <!-- The title of metric for preference of measurement system. [CHAR LIMIT=50] -->
+ <string name="metric_measurement_system">Metric</string>
+ <!-- The title of imperial US for preference of measurement system. [CHAR LIMIT=50] -->
+ <string name="us_measurement_system">Imperial (US)</string>
+ <!-- The title of imperial UK for preference of measurement system. [CHAR LIMIT=50] -->
+ <string name="uk_measurement_system">Imperial (UK)</string>
<!-- Category for the terms of address. [CHAR LIMIT=NONE]-->
<string name="category_title_terms_of_address">Additional preferences</string>
@@ -4522,6 +4530,8 @@
<string name="language_settings">Languages & input</string>
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to languages -->
<string name="languages_settings">Languages</string>
+ <!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to language and region-->
+ <string name="language_and_region_settings">Language & region</string>
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings related to keyboards -->
<string name="keyboard_settings">Keyboard</string>
<!-- Text displayed when user has restriction DISALLOW_CONFIG_LOCALE [CHAR LIMIT=NONE]-->
@@ -4588,17 +4598,17 @@
<!-- Title for the 'Bounce keys' preference switch. [CHAR LIMIT=35] -->
<string name="bounce_keys">Bounce keys</string>
<!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=300] -->
- <string name="bounce_keys_summary">The keyboard ignores quickly repeated presses of the same key </string>
+ <string name="bounce_keys_summary">Ignore quickly repeated presses of the same key</string>
<!-- Title for the 'Bounce keys' threshold dialog. [CHAR LIMIT=35] -->
- <string name="bounce_keys_dialog_title">Bounce key threshold</string>
+ <string name="bounce_keys_dialog_title">Bounce key delay</string>
<!-- Subtitle for the 'Bounce keys' threshold dialog. [CHAR LIMIT=300] -->
- <string name="bounce_keys_dialog_subtitle">Choose the duration of time your keyboard ignores repeated key presses</string>
+ <string name="bounce_keys_dialog_subtitle">Choose how long the keyboard ignores repeated keystrokes</string>
<!-- Option title for the input setting keys threshold dialog for 200 millisecond. [CHAR LIMIT=35] -->
- <string name="input_setting_keys_dialog_option_200">0.2s</string>
+ <string name="input_setting_keys_dialog_option_200">0.2 seconds</string>
<!-- Option title for the input setting keys threshold dialog for 400 millisecond. [CHAR LIMIT=35] -->
- <string name="input_setting_keys_dialog_option_400">0.4s</string>
+ <string name="input_setting_keys_dialog_option_400">0.4 seconds</string>
<!-- Option title for the input setting keys threshold dialog for 600 millisecond. [CHAR LIMIT=35] -->
- <string name="input_setting_keys_dialog_option_600">0.6s</string>
+ <string name="input_setting_keys_dialog_option_600">0.6 seconds</string>
<!-- Option title for the input setting keys threshold dialog for custom value. [CHAR LIMIT=35] -->
<string name="input_setting_keys_custom_title">Custom</string>
<!-- Option subtitle for the input setting keys threshold dialog for custom value. [CHAR LIMIT=35] -->
@@ -4607,7 +4617,7 @@
<!-- Title for the 'Slow keys' preference switch. [CHAR LIMIT=35] -->
<string name="slow_keys">Slow keys</string>
<!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=300] -->
- <string name="slow_keys_summary">Adjusts the time it takes for a key press to activate</string>
+ <string name="slow_keys_summary">Change how long you need to hold down a key before it\'s registered</string>
<!-- Title for the 'Sticky keys' preference switch. [CHAR LIMIT=35] -->
<string name="sticky_keys">Sticky keys</string>
<!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=300] -->
@@ -4658,6 +4668,8 @@
<string name="trackpad_mouse_settings">Touchpad & mouse</string>
<!-- Title for the button to trigger the 'mouse settings' page if connect with a mouse. [CHAR LIMIT=35] -->
<string name="mouse_settings">Mouse</string>
+ <!-- Summary text for mouse setting page. [CHAR LIMIT=35] -->
+ <string name="mouse_settings_summary">Pointer speed, swap buttons, button customisation</string>
<!-- Summary text for the 'trackpad settings' page. [CHAR LIMIT=100] -->
<string name="trackpad_settings_summary">Pointer speed, gestures</string>
@@ -5403,6 +5415,12 @@
<string name="accessibility_toggle_high_text_contrast_preference_title">High contrast text</string>
<!-- Summary for the accessibility preference to high contrast text. [CHAR LIMIT=NONE] -->
<string name="accessibility_toggle_high_text_contrast_preference_summary">Change text color to black or white. Maximizes contrast with the background.</string>
+ <!-- Title for the notification that high contrast text has been replaced. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_notification_high_contrast_text_title">High contrast text has been replaced</string>
+ <!-- Text content for the notification that high contrast text has been replaced. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_notification_high_contrast_text_content">Try Maximize text contrast instead. Find it in Settings.</string>
+ <!-- Action for the notification to high contrast text. [CHAR LIMIT=35] -->
+ <string name="accessibility_notification_high_contrast_text_action">Open Settings</string>
<!-- Title for the accessibility preference to high contrast text. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_maximize_text_contrast_preference_title">Maximize text contrast</string>
<!-- Summary for the accessibility preference to high contrast text. [CHAR LIMIT=NONE] -->
@@ -8814,13 +8832,13 @@
<!-- Configure notifications: Value for lockscreen notifications: show all notifications [CHAR LIMIT=60] -->
<string name="lock_screen_notifs_show_full_list">Full list</string>
- <!-- Configure notifications: Summary for lockscreen notifications: show all notifications [CHAR LIMIT=100] -->
+ <!-- Configure notifications: Summary for lockscreen notifications: show all notifications [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifs_full_list_desc">The current default placement is a full shelf and notification stack.</string>
<!-- Configure notifications: Value for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=60] -->
<string name="lock_screen_notifs_show_compact">Compact</string>
- <!-- Configure notifications: Summary for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=60] -->
+ <!-- Configure notifications: Summary for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifs_compact_desc">New notifications are collapsed into a shelf on your lockscreen.</string>
<!-- Configure notifications: Title for determining which notifications appear on the lock screen [CHAR LIMIT=60] -->
@@ -8885,16 +8903,16 @@
<!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Message asking the user how they want their profile notifications to appear when the device is locked [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifications_interstitial_message_profile">When your device is locked, how do you want profile notifications to show?</string>
- <!-- Notification Settings > Notifications on lock screen > Title for hiding seen notifications toggle. [CHAR LIMIT=30] -->
+ <!-- Notification Settings > Notifications on lock screen > Title for hiding seen notifications toggle. [CHAR LIMIT=60] -->
<string name="lock_screen_notification_hide_seen_title">Hide seen notifications</string>
- <!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=30] -->
+ <!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=100] -->
<string name="lock_screen_notification_hide_seen_summary">Seen notifications are removed from the lock screen.</string>
- <!-- Notification Settings > Notifications on lock screen > Title for hiding silent notifications toggle. [CHAR LIMIT=30] -->
+ <!-- Notification Settings > Notifications on lock screen > Title for hiding silent notifications toggle. [CHAR LIMIT=60] -->
<string name="lock_screen_notification_hide_silent_title">Hide silent notifications</string>
- <!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=30] -->
+ <!-- Notification Settings > Notifications on lock screen > Summary for hiding silent notifications toggle. [CHAR LIMIT=100] -->
<string name="lock_screen_notification_hide_silent_summary">Silent notifications and conversations are removed from the lock screen.</string>
<!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Title for the screen asking the user how they want their profile notifications to appear when the device is locked [CHAR LIMIT=30] -->
diff --git a/res/values/styles_preference.xml b/res/values/styles_preference.xml
index ec0a747..c7638d3 100644
--- a/res/values/styles_preference.xml
+++ b/res/values/styles_preference.xml
@@ -20,7 +20,6 @@
<resources>
<style name="SettingsPreferenceTheme" parent="@style/PreferenceTheme.SettingsLib">
- <item name="apnPreferenceStyle">@style/ApnPreference</item>
<item name="cardPreferenceStyle">@style/CardPreference</item>
<item name="slicePreferenceStyle">@style/SlicePreference</item>
<item name="seekBarPreferenceStyle">@style/SettingsSeekBarPreference</item>
@@ -32,10 +31,6 @@
<item name="preferenceFragmentCompatStyle">@style/SetupWizardPreferenceFragmentStyle</item>
</style>
- <style name="ApnPreference" parent="@style/Preference.Material">
- <item name="android:layout">@layout/apn_preference_layout</item>
- </style>
-
<style name="CardPreference" parent="@style/Preference.Material">
<item name="android:layout">@layout/card_preference_layout</item>
</style>
diff --git a/res/values/styles_preference_expressive.xml b/res/values/styles_preference_expressive.xml
index a6fe2f1..278fe00 100644
--- a/res/values/styles_preference_expressive.xml
+++ b/res/values/styles_preference_expressive.xml
@@ -20,7 +20,6 @@
<resources>
<style name="SettingsPreferenceTheme.Expressive" parent="@style/PreferenceTheme.SettingsLib.Expressive">
- <item name="apnPreferenceStyle">@style/ApnPreference</item>
<item name="cardPreferenceStyle">@style/CardPreference</item>
<item name="slicePreferenceStyle">@style/SlicePreference</item>
<item name="seekBarPreferenceStyle">@style/SettingsSeekBarPreference</item>
diff --git a/res/xml/accessibility_color_contrast.xml b/res/xml/accessibility_color_contrast.xml
index ffa8072..c33c5ef 100644
--- a/res/xml/accessibility_color_contrast.xml
+++ b/res/xml/accessibility_color_contrast.xml
@@ -33,8 +33,8 @@
<SwitchPreferenceCompat
android:key="toggle_high_text_contrast_preference"
android:persistent="false"
- android:summary="@string/accessibility_toggle_high_text_contrast_preference_summary"
- android:title="@string/accessibility_toggle_high_text_contrast_preference_title"
+ android:title="@string/accessibility_toggle_maximize_text_contrast_preference_title"
+ android:summary="@string/accessibility_toggle_maximize_text_contrast_preference_summary"
settings:controller=
"com.android.settings.accessibility.HighTextContrastPreferenceController" />
diff --git a/res/xml/accessibility_text_reading_options.xml b/res/xml/accessibility_text_reading_options.xml
index 795c4ffb9..40201f0 100644
--- a/res/xml/accessibility_text_reading_options.xml
+++ b/res/xml/accessibility_text_reading_options.xml
@@ -25,7 +25,7 @@
android:key="preview"
android:selectable="false"/>
- <com.android.settings.widget.LabeledSeekBarPreference
+ <com.android.settings.accessibility.AccessibilitySeekBarPreference
android:key="font_size"
android:summary="@string/short_summary_font_size"
android:title="@string/title_font_size"
@@ -35,7 +35,7 @@
settings:iconStartContentDescription="@string/font_size_make_smaller_desc"
settings:keywords="@string/keywords_font_size" />
- <com.android.settings.widget.LabeledSeekBarPreference
+ <com.android.settings.accessibility.AccessibilitySeekBarPreference
android:key="display_size"
android:summary="@string/screen_zoom_short_summary"
android:title="@string/screen_zoom_title"
@@ -56,8 +56,8 @@
<SwitchPreferenceCompat
android:key="toggle_high_text_contrast_preference"
android:persistent="false"
- android:summary="@string/accessibility_toggle_high_text_contrast_preference_summary"
- android:title="@string/accessibility_toggle_high_text_contrast_preference_title" />
+ android:title="@string/accessibility_toggle_maximize_text_contrast_preference_title"
+ android:summary="@string/accessibility_toggle_maximize_text_contrast_preference_summary" />
<com.android.settings.accessibility.TextReadingResetPreference
android:key="reset"
diff --git a/res/xml/app_data_usage.xml b/res/xml/app_data_usage.xml
index 88f60ef..3135f59 100644
--- a/res/xml/app_data_usage.xml
+++ b/res/xml/app_data_usage.xml
@@ -20,11 +20,15 @@
android:key="app_data_usage_screen"
android:title="@string/data_usage_app_summary_title">
+ <com.android.settingslib.widget.IntroPreference
+ android:key="app_header"
+ android:order="-10000"/>
+
<com.android.settings.datausage.SpinnerPreference
android:key="cycle"
settings:controller="com.android.settings.datausage.AppDataUsageCycleController" />
- <com.android.settings.spa.preference.ComposePreference
+ <com.android.settings.spa.preference.ComposeGroupSectionPreference
android:key="app_data_usage_summary"
settings:controller="com.android.settings.datausage.AppDataUsageSummaryController"/>
diff --git a/res/xml/connected_devices_advanced.xml b/res/xml/connected_devices_advanced.xml
index 779555b..f491055 100644
--- a/res/xml/connected_devices_advanced.xml
+++ b/res/xml/connected_devices_advanced.xml
@@ -42,7 +42,7 @@
android:order="-7"
android:title="@string/nfc_quick_toggle_title"
settings:controller="com.android.settings.connecteddevice.NfcAndPaymentFragmentController"
- settings:searchable="false"
+ settings:searchable="true"
settings:useAdminDisabledSummary="true"
settings:userRestriction="no_near_field_communication_radio" />
diff --git a/res/xml/date_time_prefs.xml b/res/xml/date_time_prefs.xml
index d8643be..fe0fd7e 100644
--- a/res/xml/date_time_prefs.xml
+++ b/res/xml/date_time_prefs.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
+<!-- Copyright (C) 2024 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.
@@ -94,10 +94,6 @@
android:key="time_format_preference_category"
android:title="@string/time_format_category_title"
settings:keywords="@string/keywords_time_format">
- <SwitchPreferenceCompat
- android:key="auto_24hour"
- android:title="@string/date_time_24hour_auto"
- settings:controller="com.android.settings.datetime.AutoTimeFormatPreferenceController" />
<SwitchPreferenceCompat
android:key="24 hour"
diff --git a/res/xml/date_time_prefs_revamped.xml b/res/xml/date_time_prefs_revamped.xml
deleted file mode 100644
index fe0fd7e..0000000
--- a/res/xml/date_time_prefs_revamped.xml
+++ /dev/null
@@ -1,104 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2024 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/date_and_time"
- settings:keywords="@string/keywords_date_and_time">
-
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="auto_time"
- android:title="@string/date_time_auto"
- android:summary="@string/summary_placeholder"
- settings:userRestriction="no_config_date_time"
- settings:controller="com.android.settings.datetime.AutoTimePreferenceController" />
-
- <com.android.settingslib.RestrictedPreference
- android:key="date"
- android:title="@string/date_time_set_date_title"
- android:summary="@string/summary_placeholder"
- settings:userRestriction="no_config_date_time"
- settings:controller="com.android.settings.datetime.DatePreferenceController" />
-
- <com.android.settingslib.RestrictedPreference
- android:key="time"
- android:title="@string/date_time_set_time_title"
- android:summary="@string/summary_placeholder"
- settings:userRestriction="no_config_date_time"
- settings:controller="com.android.settings.datetime.TimePreferenceController" />
-
- <PreferenceCategory
- android:key="timezone_preference_category"
- android:title="@string/date_time_set_timezone_title">
-
- <com.android.settingslib.RestrictedSwitchPreference
- android:key="auto_zone"
- android:title="@string/zone_auto_title"
- android:summary="@string/summary_placeholder"
- settings:userRestriction="no_config_date_time"
- settings:controller="com.android.settings.datetime.AutoTimeZonePreferenceController" />
-
- <com.android.settingslib.widget.BannerMessagePreference
- android:key="location_time_zone_detection_status"
- android:title="@string/location_time_zone_detection_status_title"
- settings:controller="com.android.settings.datetime.LocationProviderStatusPreferenceController"/>
-
- <!-- This preference gets removed if location-based time zone detection is not supported -->
- <SwitchPreferenceCompat
- android:key="location_time_zone_detection"
- android:title="@string/location_time_zone_detection_toggle_title"
- android:summary="@string/summary_placeholder"
- settings:controller="com.android.settings.datetime.LocationTimeZoneDetectionPreferenceController"/>
-
- <com.android.settingslib.RestrictedPreference
- android:key="timezone"
- android:title="@string/date_time_set_timezone_title"
- android:summary="@string/summary_placeholder"
- android:fragment="com.android.settings.datetime.timezone.TimeZoneSettings"
- settings:userRestriction="no_config_date_time"
- settings:keywords="@string/keywords_time_zone"
- settings:controller="com.android.settings.datetime.TimeZonePreferenceController" />
-
- </PreferenceCategory>
-
- <!-- An optional preference category for feedback. Only displayed up if enabled via flags and config. -->
- <PreferenceCategory
- android:key="time_feedback_preference_category"
- android:title="@string/time_feedback_category_title"
- settings:keywords="@string/keywords_time_feedback_category"
- settings:controller="com.android.settings.datetime.TimeFeedbackPreferenceCategoryController">
-
- <Preference
- android:key="time_feedback"
- android:title="@string/time_feedback_title"
- settings:keywords="@string/keywords_time_feedback"
- settings:controller="com.android.settings.datetime.TimeFeedbackPreferenceController" />
-
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="time_format_preference_category"
- android:title="@string/time_format_category_title"
- settings:keywords="@string/keywords_time_format">
-
- <SwitchPreferenceCompat
- android:key="24 hour"
- android:title="@string/date_time_24hour"
- settings:controller="com.android.settings.datetime.TimeFormatPreferenceController" />
- </PreferenceCategory>
-
-</PreferenceScreen>
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 25bc062..2849238 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -569,6 +569,13 @@
android:entries="@array/overlay_display_devices_entries"
android:entryValues="@array/overlay_display_devices_values" />
+ <ListPreference
+ android:entries="@array/shade_display_awareness_entries"
+ android:entryValues="@array/shade_display_awareness_values"
+ android:key="shade_display_awareness"
+ android:summary="@string/summary_placeholder"
+ android:title="@string/shade_display_awareness_title" />
+
<com.android.settings.display.DensityPreference
android:key="density"
android:title="@string/developer_smallest_width" />
diff --git a/res/xml/keyboard_layout_picker_fragment.xml b/res/xml/keyboard_layout_picker_fragment.xml
deleted file mode 100644
index d1bf971..0000000
--- a/res/xml/keyboard_layout_picker_fragment.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:settings="http://schemas.android.com/apk/res-auto"
- android:key="keyboard_layout_picker"
- android:title="@string/keyboard_layout_picker_title"
- settings:controller="com.android.settings.inputmethod.KeyboardLayoutPickerController">
-
-</PreferenceScreen>
diff --git a/res/xml/language_and_region_settings.xml b/res/xml/language_and_region_settings.xml
new file mode 100644
index 0000000..5626f22
--- /dev/null
+++ b/res/xml/language_and_region_settings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2024 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/language_and_region_settings"
+ android:key="language_and_region_settings">
+ <PreferenceCategory
+ android:key="languages_category"
+ android:title="@string/locale_picker_category_title">
+ <Preference
+ android:key="phone_language"
+ android:title="@string/system_language"
+ android:fragment="com.android.settings.localepicker.LocaleListEditor"
+ settings:controller="com.android.settings.language.PhoneLanguagePreferenceController" />
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="more_language_settings_category"
+ android:title="@string/more_language_settings_category"
+ settings:controller="com.android.settings.language.MoreLanguagesSettingsCategoryController">
+ <Preference
+ android:key="apps_language_in_more_language_settings"
+ android:title="@string/app_locales_picker_menu_title"
+ android:summary="@string/app_locale_picker_summary"
+ android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
+ settings:controller="com.android.settings.applications.appinfo.NewAppsLocalePreferenceController">
+ <extra
+ android:name="classname"
+ android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
+ </Preference>
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="regional_preferences_category"
+ android:title="@string/regional_preferences_category_title"
+ settings:controller="com.android.settings.regionalpreferences.RegionalPreferencesCategoryController">
+
+ <Preference
+ android:key="temperature_preference"
+ android:title="@string/temperature_preferences_title"
+ android:summary="@string/default_string_of_regional_preference"
+ settings:controller="com.android.settings.regionalpreferences.NewTemperatureUnitController"
+ settings:fragment="com.android.settings.regionalpreferences.TemperatureUnitFragment">
+ <extra
+ android:name="arg_key_regional_preference"
+ android:value="mu"/>
+ </Preference>
+
+ <Preference
+ android:key="key_measurement_system"
+ android:title="@string/measurement_system_preferences_title"
+ android:summary="@string/default_string_of_regional_preference"
+ settings:controller="com.android.settings.regionalpreferences.MeasurementSystemController"
+ settings:fragment="com.android.settings.regionalpreferences.MeasurementSystemItemFragment">
+ <extra
+ android:name="arg_key_regional_preference"
+ android:value="ms"/>
+ </Preference>
+
+ <Preference
+ android:key="first_day_of_week_preference"
+ android:title="@string/first_day_of_week_preferences_title"
+ android:summary="@string/default_string_of_regional_preference"
+ settings:controller="com.android.settings.regionalpreferences.NewFirstDayOfWeekController"
+ settings:fragment="com.android.settings.regionalpreferences.FirstDayOfWeekItemFragment">
+ <extra
+ android:name="arg_key_regional_preference"
+ android:value="fw"/>
+ </Preference>
+
+ <Preference
+ android:key="numbering_system_preference"
+ android:title="@string/numbers_preferences_title"
+ android:summary="@string/default_string_of_regional_preference"
+ settings:controller="com.android.settings.regionalpreferences.NewNumberingSystemController"
+ settings:fragment="com.android.settings.regionalpreferences.NumberingPreferencesFragment">
+ <extra
+ android:name="arg_key_regional_preference"
+ android:value="arg_value_language_select"/>
+ </Preference>
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="speech_category"
+ android:title="@string/speech_category_title">
+ <com.android.settings.widget.GearPreference
+ android:key="voice_input_settings"
+ android:title="@string/voice_input_settings_title"
+ android:fragment="com.android.settings.language.DefaultVoiceInputPicker" />
+
+ <Preference
+ android:key="on_device_recognition_settings"
+ android:title="@string/on_device_recognition_settings_title"
+ android:summary="@string/on_device_recognition_settings_summary"
+ settings:controller=
+ "com.android.settings.language.OnDeviceRecognitionPreferenceController" />
+
+ <Preference
+ android:key="tts_settings_summary"
+ android:title="@string/tts_settings_title"
+ android:fragment="com.android.settings.tts.TextToSpeechSettings"
+ settings:searchable="false"/>
+ </PreferenceCategory>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="new_regional_pref_footer"
+ android:title="@string/title_regional_pref_footer"
+ android:selectable="false"
+ settings:searchable="false"
+ settings:controller="com.android.settings.regionalpreferences.NewRegionalFooterPreferenceController"/>
+</PreferenceScreen>
diff --git a/res/xml/language_settings.xml b/res/xml/language_settings.xml
index f9f423e..eccbbc1 100644
--- a/res/xml/language_settings.xml
+++ b/res/xml/language_settings.xml
@@ -50,61 +50,6 @@
</PreferenceCategory>
<PreferenceCategory
- android:key="more_language_settings_category"
- android:title="@string/more_language_settings_category"
- settings:controller="com.android.settings.language.MoreLanguagesSettingsCategoryController">
- <Preference
- android:key="apps_language_in_more_language_settings"
- android:title="@string/app_locales_picker_menu_title"
- android:summary="@string/app_locale_picker_summary"
- android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
- settings:controller="com.android.settings.applications.appinfo.NewAppsLocalePreferenceController">
- <extra
- android:name="classname"
- android:value="com.android.settings.applications.appinfo.AppLocaleDetails" />
- </Preference>
- </PreferenceCategory>
-
- <PreferenceCategory
- android:key="regional_preferences_category"
- android:title="@string/regional_preferences_category_title"
- settings:controller="com.android.settings.regionalpreferences.RegionalPreferencesCategoryController">
-
- <Preference
- android:key="temperature_preference"
- android:title="@string/temperature_preferences_title"
- android:summary="@string/default_string_of_regional_preference"
- settings:controller="com.android.settings.regionalpreferences.NewTemperatureUnitController"
- settings:fragment="com.android.settings.regionalpreferences.TemperatureUnitFragment">
- <extra
- android:name="arg_key_regional_preference"
- android:value="mu"/>
- </Preference>
-
- <Preference
- android:key="first_day_of_week_preference"
- android:title="@string/first_day_of_week_preferences_title"
- android:summary="@string/default_string_of_regional_preference"
- settings:controller="com.android.settings.regionalpreferences.NewFirstDayOfWeekController"
- settings:fragment="com.android.settings.regionalpreferences.FirstDayOfWeekItemFragment">
- <extra
- android:name="arg_key_regional_preference"
- android:value="fw"/>
- </Preference>
-
- <Preference
- android:key="numbering_system_preference"
- android:title="@string/numbers_preferences_title"
- android:summary="@string/default_string_of_regional_preference"
- settings:controller="com.android.settings.regionalpreferences.NewNumberingSystemController"
- settings:fragment="com.android.settings.regionalpreferences.NumberingPreferencesFragment">
- <extra
- android:name="arg_key_regional_preference"
- android:value="arg_value_language_select"/>
- </Preference>
- </PreferenceCategory>
-
- <PreferenceCategory
android:key="speech_category"
android:title="@string/speech_category_title">
<com.android.settings.widget.GearPreference
@@ -125,11 +70,4 @@
android:fragment="com.android.settings.tts.TextToSpeechSettings"
settings:searchable="false"/>
</PreferenceCategory>
-
- <com.android.settingslib.widget.FooterPreference
- android:key="new_regional_pref_footer"
- android:title="@string/title_regional_pref_footer"
- android:selectable="false"
- settings:searchable="false"
- settings:controller="com.android.settings.regionalpreferences.NewRegionalFooterPreferenceController"/>
</PreferenceScreen>
diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml
index a1a2276..8c948ff 100644
--- a/res/xml/mobile_network_settings.xml
+++ b/res/xml/mobile_network_settings.xml
@@ -18,7 +18,7 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="mobile_network_pref_screen">
- <com.android.settings.spa.preference.ComposeMainSwitchPreference
+ <com.android.settings.spa.preference.ComposeGroupSectionPreference
android:key="use_sim_switch"
settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/>
diff --git a/res/xml/regional_preferences_measurement_system.xml b/res/xml/regional_preferences_measurement_system.xml
new file mode 100644
index 0000000..73ac03d
--- /dev/null
+++ b/res/xml/regional_preferences_measurement_system.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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/measurement_system_preferences_title"
+ android:key="regional_preference_measurement_system">
+
+ <com.android.settingslib.widget.TopIntroPreference
+ android:key="measurement_system_intro"
+ android:title="@string/regional_preferences_option_page_sub_title"
+ android:persistent="false" />
+
+ <PreferenceCategory
+ android:key="measurement_system_item_category"
+ android:title="@string/summary_placeholder"
+ android:layout="@layout/preference_category_no_label"
+ settings:controller="com.android.settings.regionalpreferences.MeasurementSystemItemCategoryController"/>
+
+</PreferenceScreen>
diff --git a/res/xml/system_dashboard_fragment.xml b/res/xml/system_dashboard_fragment.xml
index 83cdf64..a9d7444 100644
--- a/res/xml/system_dashboard_fragment.xml
+++ b/res/xml/system_dashboard_fragment.xml
@@ -30,6 +30,15 @@
settings:controller="com.android.settings.language.LanguagePreferenceController"/>
<Preference
+ android:key="language_and_region_settings"
+ android:title="@string/language_and_region_settings"
+ android:summary="@string/languages_setting_summary"
+ android:icon="@drawable/ic_settings_languages"
+ android:order="-260"
+ android:fragment="com.android.settings.language.LanguageAndRegionSettings"
+ settings:controller="com.android.settings.language.LanguageAndRegionPreferenceController"/>
+
+ <Preference
android:key="Keyboard_settings"
android:title="@string/keyboard_settings"
android:icon="@drawable/ic_settings_keyboards"
@@ -47,6 +56,24 @@
settings:controller="com.android.settings.inputmethod.TouchpadAndMouseSettingsController"/>
<Preference
+ android:key="touchpad_settings"
+ android:title="@string/trackpad_settings"
+ android:summary="@string/trackpad_settings_summary"
+ android:icon="@drawable/ic_settings_trackpad"
+ android:order="-253"
+ android:fragment="com.android.settings.inputmethod.TouchpadSettingFragment"
+ settings:controller="com.android.settings.inputmethod.TouchpadSettingsController"/>
+
+ <Preference
+ android:key="mouse_settings"
+ android:title="@string/mouse_settings"
+ android:summary="@string/mouse_settings_summary"
+ android:icon="@drawable/ic_settings_mouse"
+ android:order="-252"
+ android:fragment="com.android.settings.inputmethod.MouseSettingFragment"
+ settings:controller="com.android.settings.inputmethod.MouseSettingsController"/>
+
+ <Preference
android:key="gesture_settings"
android:title="@string/gesture_preference_title"
android:icon="@drawable/ic_settings_gestures"
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index d5cf9aa..aa52a2a 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -131,6 +131,9 @@
public static class AccessibilityDaltonizerSettingsActivity extends SettingsActivity { /* empty */ }
/** Activity for lockscreen settings. */
public static class LockScreenSettingsActivity extends SettingsActivity { /* empty */ }
+ /** Activity for notifications on lockscreen settings. */
+ public static class LockScreenNotificationsSettingsActivity
+ extends SettingsActivity { /* empty */ }
/** Activity for bluetooth pairing settings. */
public static class BlueToothPairingActivity extends SettingsActivity { /* empty */ }
/** Activity for Reduce Bright Colors. */
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index c81d504..4de04d3 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -311,7 +311,10 @@
}
setContentView(R.layout.settings_main_prefs);
- mMainSwitch = findViewById(R.id.switch_bar);
+
+ mMainSwitch = SettingsThemeHelper.isExpressiveTheme(this)
+ ? findViewById(R.id.expressive_switch_bar)
+ : findViewById(R.id.switch_bar);
if (mMainSwitch != null) {
mMainSwitch.setMetricsCategory(lookupMetricsCategory());
mMainSwitch.setTranslationZ(findViewById(R.id.main_content).getTranslationZ() + 1);
diff --git a/src/com/android/settings/SettingsService.kt b/src/com/android/settings/SettingsService.kt
index 70713c3..1783a7d 100644
--- a/src/com/android/settings/SettingsService.kt
+++ b/src/com/android/settings/SettingsService.kt
@@ -41,7 +41,7 @@
override fun hasPermission(
application: Application,
- myUid: Int,
+ callingPid: Int,
callingUid: Int,
request: PreferenceSetterRequest,
) = true
@@ -52,7 +52,7 @@
override fun hasPermission(
application: Application,
- myUid: Int,
+ callingPid: Int,
callingUid: Int,
request: PreferenceGetterRequest,
) = true
diff --git a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
index 09e2d97..3bda864 100644
--- a/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityHearingAidsFragment.java
@@ -97,17 +97,6 @@
}
@Override
- protected ComponentName getTileComponentName() {
- return AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME;
- }
-
- @Override
- protected CharSequence getTileTooltipContent(int type) {
- // No tooltip to be shown
- return null;
- }
-
- @Override
protected boolean showGeneralCategory() {
// Have static preference under dynamically created PreferenceCategory KEY_GENERAL_CATEGORY.
// In order to modify that, we need to use our own PreferenceCategory for this page.
diff --git a/src/com/android/settings/accessibility/AccessibilitySeekBarPreference.kt b/src/com/android/settings/accessibility/AccessibilitySeekBarPreference.kt
new file mode 100644
index 0000000..b98c0e2
--- /dev/null
+++ b/src/com/android/settings/accessibility/AccessibilitySeekBarPreference.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.accessibility
+
+import android.content.Context
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.AttributeSet
+import com.android.settings.widget.LabeledSeekBarPreference
+
+/**
+ * Add a custom AccessibilitySeekBarPreference with tool tip window for font size and display size.
+ */
+open class AccessibilitySeekBarPreference(context: Context, attrs: AttributeSet?) :
+ LabeledSeekBarPreference(context, attrs) {
+
+ var needsQSTooltipReshow = false
+ private var tooltipWindow: AccessibilityQuickSettingsTooltipWindow? = null
+
+ override fun onSaveInstanceState(): Parcelable {
+ val state = Bundle()
+ state.putParcelable(null, super.onSaveInstanceState())
+ if (needsQSTooltipReshow || tooltipWindow?.isShowing == true) {
+ state.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true)
+ }
+ return state
+ }
+
+ override fun onRestoreInstanceState(state: Parcelable?) {
+ val bundle = state as Bundle
+ super.onRestoreInstanceState(bundle.getParcelable(null, Parcelable::class.java))
+ if (bundle.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
+ needsQSTooltipReshow = bundle.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW)
+ }
+ }
+
+ /** To generate a tooltip window and return it. */
+ fun createTooltipWindow(): AccessibilityQuickSettingsTooltipWindow =
+ AccessibilityQuickSettingsTooltipWindow(context).also { tooltipWindow = it }
+
+ /** To dismiss the tooltip window. */
+ fun dismissTooltip() {
+ val tooltip = tooltipWindow
+ if (tooltip?.isShowing == true) {
+ tooltip.dismiss()
+ tooltipWindow = null
+ }
+ }
+
+ companion object {
+ private const val KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow"
+ }
+}
diff --git a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
index 23c3fd4..886c949 100644
--- a/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragment.java
@@ -20,10 +20,8 @@
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_GENERAL_CATEGORY;
-import static com.android.settings.accessibility.ToggleFeaturePreferenceFragment.KEY_SAVED_QS_TOOLTIP_TYPE;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
@@ -43,8 +41,8 @@
import androidx.preference.PreferenceScreen;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
-import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.dashboard.RestrictedDashboardFragment;
@@ -60,16 +58,12 @@
public abstract class AccessibilityShortcutPreferenceFragment extends RestrictedDashboardFragment
implements ShortcutPreference.OnClickCallback {
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
- protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
protected ShortcutPreference mShortcutPreference;
protected Dialog mDialog;
private AccessibilityManager.TouchExplorationStateChangeListener
mTouchExplorationStateChangeListener;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
- private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
- private boolean mNeedsQSTooltipReshow = false;
- private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
public AccessibilityShortcutPreferenceFragment(String restrictionKey) {
super(restrictionKey);
@@ -81,26 +75,10 @@
/** Returns the accessibility feature name. */
protected abstract CharSequence getLabelName();
- /** Returns the accessibility tile component name. */
- protected abstract ComponentName getTileComponentName();
-
- /** Returns the accessibility tile tooltip content. */
- protected abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Restore the user shortcut type and tooltip.
- if (savedInstanceState != null) {
- if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
- mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
- }
- if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
- mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
- }
- }
-
final int resId = getPreferenceScreenResId();
if (resId <= 0) {
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
@@ -142,21 +120,6 @@
}
@Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // Reshow tooltip when activity recreate, such as rotate device.
- if (mNeedsQSTooltipReshow) {
- view.post(() -> {
- final Activity activity = getActivity();
- if (activity != null && !activity.isFinishing()) {
- showQuickSettingsTooltipIfNeeded();
- }
- });
- }
- }
-
- @Override
public void onResume() {
super.onResume();
@@ -178,16 +141,6 @@
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
- outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
- }
- super.onSaveInstanceState(outState);
- }
-
- @Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
@@ -289,7 +242,6 @@
*/
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
dialog.dismiss();
- showQuickSettingsTooltipIfNeeded();
}
@VisibleForTesting
@@ -357,32 +309,12 @@
final int shortcutTypes = getUserPreferredShortcutTypes();
mShortcutPreference.setChecked(
- AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
- getComponentName()));
+ ShortcutUtils.isShortcutContained(
+ getPrefContext(), shortcutTypes, getComponentName().flattenToString()));
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
/**
- * Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
- * shows once.
- *
- * @param type The quick settings tooltip type
- */
- protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
- mNeedsQSTooltipType = type;
- showQuickSettingsTooltipIfNeeded();
- }
-
- /**
- * @deprecated made obsolete by quick settings rollout.
- *
- * (TODO 367414968: finish removal.)
- */
- @Deprecated
- private void showQuickSettingsTooltipIfNeeded() {
- }
-
- /**
* Returns the user preferred shortcut types or the default shortcut types if not set
*/
@ShortcutConstants.UserShortcutType
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index 772ed96..04c4702 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -21,7 +21,6 @@
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
-import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.DEFAULT;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
@@ -37,7 +36,6 @@
import android.graphics.Rect;
import android.icu.text.CaseMap;
import android.os.Build;
-import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.TypedValue;
@@ -195,52 +193,6 @@
}
/**
- * Returns if component name existed in one of {@code shortcutTypes} string in Settings.
- *
- * @param context The current context.
- * @param shortcutTypes A combination of {@link UserShortcutType}.
- * @param componentName The component name that need to be checked existed in Settings.
- * @return {@code true} if componentName existed in Settings.
- */
- static boolean hasValuesInSettings(Context context, int shortcutTypes,
- @NonNull ComponentName componentName) {
- for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
- if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
- if ((shortcutType & GESTURE) == GESTURE) {
- continue;
- }
- }
- if ((shortcutTypes & shortcutType) == shortcutType
- && hasValueInSettings(context, shortcutType, componentName)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Returns if component name existed in {@code shortcutType} string Settings.
- *
- * @param context The current context.
- * @param shortcutType The preferred shortcut type user selected.
- * @param componentName The component name that need to be checked existed in Settings.
- * @return {@code true} if componentName existed in Settings.
- *
- * @deprecated use
- * {@link ShortcutUtils#isShortcutContained(Context, int, String)} instead.
- *
- * (TODO 367414968: finish removal.)
- */
- @Deprecated
- @VisibleForTesting
- static boolean hasValueInSettings(Context context, @UserShortcutType int shortcutType,
- @NonNull ComponentName componentName) {
- return ShortcutUtils.getShortcutTargetsFromSettings(
- context, shortcutType, UserHandle.myUserId()
- ).contains(componentName.flattenToString());
- }
-
- /**
* Gets the corresponding user shortcut type of a given accessibility service.
*
* @param context The current context.
@@ -250,14 +202,15 @@
*/
static int getUserShortcutTypesFromSettings(Context context,
@NonNull ComponentName componentName) {
- int shortcutTypes = DEFAULT;
+ int shortcutTypes = UserShortcutType.DEFAULT;
for (int shortcutType : AccessibilityUtil.SHORTCUTS_ORDER_IN_UI) {
if (!android.provider.Flags.a11yStandaloneGestureEnabled()) {
if ((shortcutType & GESTURE) == GESTURE) {
continue;
}
}
- if (hasValueInSettings(context, shortcutType, componentName)) {
+ if (ShortcutUtils.isShortcutContained(
+ context, shortcutType, componentName.flattenToString())) {
shortcutTypes |= shortcutType;
}
}
diff --git a/src/com/android/settings/accessibility/HighContrastTextMigrationReceiver.java b/src/com/android/settings/accessibility/HighContrastTextMigrationReceiver.java
new file mode 100644
index 0000000..7743a1f
--- /dev/null
+++ b/src/com/android/settings/accessibility/HighContrastTextMigrationReceiver.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.accessibility;
+
+import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+
+import android.annotation.IntDef;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.graphics.hwui.flags.Flags;
+import com.android.settings.R;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Handling smooth migration to the new high contrast text appearance
+ */
+public class HighContrastTextMigrationReceiver extends BroadcastReceiver {
+ private static final String TAG = HighContrastTextMigrationReceiver.class.getSimpleName();
+ @VisibleForTesting
+ static final String NOTIFICATION_CHANNEL = "accessibility_notification_channel";
+ @VisibleForTesting
+ static final String ACTION_RESTORED =
+ "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED";
+ @VisibleForTesting
+ static final int NOTIFICATION_ID = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ PromptState.UNKNOWN,
+ PromptState.PROMPT_SHOWN,
+ PromptState.PROMPT_UNNECESSARY,
+ })
+ public @interface PromptState {
+ int UNKNOWN = 0;
+ int PROMPT_SHOWN = 1;
+ int PROMPT_UNNECESSARY = 2;
+ }
+
+ @Override
+ public void onReceive(@NonNull Context context, @NonNull Intent intent) {
+ if (!Flags.highContrastTextSmallTextRect()) {
+ return;
+ }
+
+ if (ACTION_RESTORED.equals(intent.getAction())) {
+ Log.i(TAG, "HCT attempted to be restored from backup; showing notification for userId: "
+ + context.getUserId());
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ PromptState.PROMPT_SHOWN);
+ showNotification(context);
+ } else if (Intent.ACTION_PRE_BOOT_COMPLETED.equals(intent.getAction())) {
+ final boolean hasSeenPromptIfNecessary = Settings.Secure.getInt(
+ context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, PromptState.UNKNOWN)
+ != PromptState.UNKNOWN;
+ if (hasSeenPromptIfNecessary) {
+ Log.i(TAG, "Has seen HCT prompt if necessary; skip HCT migration for userId: "
+ + context.getUserId());
+ return;
+ }
+
+ final boolean isHctEnabled = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF) == ON;
+ if (isHctEnabled) {
+ Log.i(TAG, "HCT enabled before OTA update; performing migration for userId: "
+ + context.getUserId());
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+ OFF);
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ PromptState.PROMPT_SHOWN);
+ showNotification(context);
+ } else {
+ Log.i(TAG,
+ "HCT was not enabled before OTA update; not performing migration for "
+ + "userId: " + context.getUserId());
+ Settings.Secure.putInt(context.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ PromptState.PROMPT_UNNECESSARY);
+ }
+ }
+ }
+
+ private void showNotification(Context context) {
+ Notification.Builder notificationBuilder = new Notification.Builder(context,
+ NOTIFICATION_CHANNEL)
+ .setSmallIcon(R.drawable.ic_settings_24dp)
+ .setContentTitle(context.getString(
+ R.string.accessibility_notification_high_contrast_text_title))
+ .setContentText(context.getString(
+ R.string.accessibility_notification_high_contrast_text_content))
+ .setAutoCancel(true);
+
+ Intent settingsIntent = new Intent(Settings.ACTION_TEXT_READING_SETTINGS);
+ settingsIntent.setPackage(context.getPackageName());
+ if (settingsIntent.resolveActivity(context.getPackageManager()) != null) {
+ Bundle fragmentArgs = new Bundle();
+ fragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY,
+ TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY);
+ settingsIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
+ PendingIntent settingsPendingIntent = PendingIntent.getActivity(context,
+ /* requestCode = */ 0, settingsIntent, PendingIntent.FLAG_IMMUTABLE);
+
+ Notification.Action settingsAction = new Notification.Action.Builder(
+ /* icon= */ null,
+ context.getString(
+ R.string.accessibility_notification_high_contrast_text_action),
+ settingsPendingIntent
+ ).build();
+
+ notificationBuilder.addAction(settingsAction);
+ }
+
+ NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ NotificationChannel notificationChannel = new NotificationChannel(
+ NOTIFICATION_CHANNEL,
+ context.getString(R.string.accessibility_settings),
+ NotificationManager.IMPORTANCE_LOW);
+ notificationManager.createNotificationChannel(notificationChannel);
+ notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
+ }
+}
diff --git a/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java b/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
index 7a3f4f6..c28af91 100644
--- a/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
+++ b/src/com/android/settings/accessibility/HighTextContrastPreferenceController.java
@@ -22,6 +22,7 @@
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
+import com.android.graphics.hwui.flags.Flags;
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.TogglePreferenceController;
@@ -60,6 +61,20 @@
isChecked ? 1 : 0,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
+ if (Flags.highContrastTextSmallTextRect()) {
+ // Set PROMPT_UNNECESSARY when the user modifies the HighContrastText setting
+ // This is needed for the following scenario:
+ // On Android 16, create secondary user, ACTION_PRE_BOOT_COMPLETED won't be sent to
+ // the secondary user. The user enables HCT.
+ // When updating OS to Android 17, ACTION_PRE_BOOT_COMPLETED will be sent to the
+ // secondary user when switch to the secondary user.
+ // If the prompt status is not updated in Android 16, we would automatically disable
+ // HCT and show the HCT prompt, which is an undesired behavior.
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
+ HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY);
+ }
+
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, (isChecked ? 1 : 0));
}
diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
index e49078b..f729cc7 100644
--- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java
@@ -72,11 +72,6 @@
}
@Override
- protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- // Do nothing.
- }
-
- @Override
protected void onProcessArguments(Bundle arguments) {
super.onProcessArguments(arguments);
mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
diff --git a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
index 7f10af7..3c62231 100644
--- a/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
+++ b/src/com/android/settings/accessibility/PreviewSizeSeekBarController.java
@@ -19,7 +19,6 @@
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
-import android.os.Bundle;
import android.os.Handler;
import android.widget.SeekBar;
@@ -28,31 +27,27 @@
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
-import com.android.settings.widget.LabeledSeekBarPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
-import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.Optional;
/**
- * The controller of {@link LabeledSeekBarPreference} that listens to display size and font size
- * settings changes and updates preview size threshold smoothly.
+ * The controller of {@link AccessibilitySeekBarPreference} that listens to display size and font
+ * size settings changes and updates preview size threshold smoothly.
*/
abstract class PreviewSizeSeekBarController extends BasePreferenceController implements
- TextReadingResetController.ResetStateListener, LifecycleObserver, OnCreate,
- OnDestroy, OnSaveInstanceState {
+ TextReadingResetController.ResetStateListener, LifecycleObserver, OnStart, OnStop,
+ OnDestroy {
private final PreviewSizeData<? extends Number> mSizeData;
- private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private boolean mSeekByTouch;
private Optional<ProgressInteractionListener> mInteractionListener = Optional.empty();
- private LabeledSeekBarPreference mSeekBarPreference;
+ private AccessibilitySeekBarPreference mSeekBarPreference;
private int mLastProgress;
- private boolean mNeedsQSTooltipReshow = false;
- private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private final Handler mHandler;
private String[] mStateLabels = null;
@@ -101,30 +96,21 @@
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- // Restore the tooltip.
- if (savedInstanceState != null
- && savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
- mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
+ public void onStart() {
+ if (mSeekBarPreference.getNeedsQSTooltipReshow()) {
+ mHandler.post(this::showQuickSettingsTooltipIfNeeded);
}
}
@Override
+ public void onStop() {
+ // all the messages/callbacks will be removed.
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ @Override
public void onDestroy() {
- // remove runnables in the queue.
- mHandler.removeCallbacksAndMessages(null);
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (isTooltipWindowShowing) {
- mTooltipWindow.dismiss();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
- outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- }
+ mSeekBarPreference.dismissTooltip();
}
void setInteractionListener(ProgressInteractionListener interactionListener) {
@@ -148,9 +134,6 @@
mSeekBarPreference.setProgress(initialIndex);
mSeekBarPreference.setContinuousUpdates(true);
mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
- if (mNeedsQSTooltipReshow) {
- mHandler.post(this::showQuickSettingsTooltipIfNeeded);
- }
setSeekbarStateDescription(mSeekBarPreference.getProgress());
}
@@ -216,7 +199,8 @@
return;
}
- if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
+ if (!mSeekBarPreference.getNeedsQSTooltipReshow()
+ && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
mContext, tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
@@ -228,14 +212,15 @@
// is not ready when we would like to show the tooltip. If the seekbar is not ready,
// we give up showing the tooltip and also do not reshow it in the future.
if (mSeekBarPreference.getSeekbar() != null) {
- mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
- mTooltipWindow.setup(getTileTooltipContent(),
+ final AccessibilityQuickSettingsTooltipWindow tooltipWindow =
+ mSeekBarPreference.createTooltipWindow();
+ tooltipWindow.setup(getTileTooltipContent(),
R.drawable.accessibility_auto_added_qs_tooltip_illustration);
- mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
+ tooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
}
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext,
tileComponentName);
- mNeedsQSTooltipReshow = false;
+ mSeekBarPreference.setNeedsQSTooltipReshow(false);
}
/** Returns the accessibility Quick Settings tile component name. */
diff --git a/src/com/android/settings/accessibility/RemoveAnimationsPreference.kt b/src/com/android/settings/accessibility/RemoveAnimationsPreference.kt
index 65519e9..ddf2590 100644
--- a/src/com/android/settings/accessibility/RemoveAnimationsPreference.kt
+++ b/src/com/android/settings/accessibility/RemoveAnimationsPreference.kt
@@ -65,11 +65,15 @@
override fun storage(context: Context): KeyValueStore = RemoveAnimationsStorage(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/accessibility/TextReadingPreviewController.java b/src/com/android/settings/accessibility/TextReadingPreviewController.java
index a983105..99f1f3f 100644
--- a/src/com/android/settings/accessibility/TextReadingPreviewController.java
+++ b/src/com/android/settings/accessibility/TextReadingPreviewController.java
@@ -33,7 +33,6 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
import com.android.settings.display.PreviewPagerAdapter;
-import com.android.settings.widget.LabeledSeekBarPreference;
import java.util.Objects;
@@ -58,8 +57,8 @@
private int mLastDisplayProgress;
private long mLastCommitTime;
private TextReadingPreviewPreference mPreviewPreference;
- private LabeledSeekBarPreference mFontSizePreference;
- private LabeledSeekBarPreference mDisplaySizePreference;
+ private AccessibilitySeekBarPreference mFontSizePreference;
+ private AccessibilitySeekBarPreference mDisplaySizePreference;
@EntryPoint
private int mEntryPoint;
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index e286225..b779f9d 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -107,10 +107,6 @@
if (enabled == isEnabled) {
return;
}
-
- if (enabled) {
- showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
- }
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
index 5b2df5a..fe51e69 100644
--- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@@ -158,10 +158,6 @@
if (enabled == isEnabled) {
return;
}
-
- if (enabled) {
- showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
- }
logAccessibilityServiceEnabled(mComponentName, enabled);
Settings.Secure.putInt(getContentResolver(), ENABLED, enabled ? ON : OFF);
}
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index 65a1cd4..91f8c4f 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -21,7 +21,6 @@
import static com.android.settings.accessibility.AccessibilityDialogUtils.DialogEnums;
import static com.android.settings.accessibility.AccessibilityUtil.getShortcutSummaryList;
-import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
@@ -57,6 +56,7 @@
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
@@ -85,8 +85,6 @@
public static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
protected static final String KEY_TOP_INTRO_PREFERENCE = "top_intro";
protected static final String KEY_HTML_DESCRIPTION_PREFERENCE = "html_description";
- protected static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
- protected static final String KEY_SAVED_QS_TOOLTIP_TYPE = "qs_tooltip_type";
protected static final String KEY_ANIMATED_IMAGE = "animated_image";
// For html description of accessibility service, must follow the rule, such as
// <img src="R.drawable.fileName"/>, a11y settings will get the resources successfully.
@@ -112,10 +110,6 @@
private CharSequence mDescription;
private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private AccessibilitySettingsContentObserver mSettingsContentObserver;
-
- private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
- private boolean mNeedsQSTooltipReshow = false;
- private int mNeedsQSTooltipType = QuickSettingsTooltipType.GUIDE_TO_EDIT;
private ImageView mImageGetterCacheView;
protected final Html.ImageGetter mImageGetter = (String str) -> {
if (str != null && str.startsWith(IMG_PREFIX)) {
@@ -133,16 +127,6 @@
super.onCreate(savedInstanceState);
onProcessArguments(getArguments());
- // Restore the user shortcut type and tooltip.
- if (savedInstanceState != null) {
- if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
- mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
- }
- if (savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_TYPE)) {
- mNeedsQSTooltipType = savedInstanceState.getInt(KEY_SAVED_QS_TOOLTIP_TYPE);
- }
- }
-
final int resId = getPreferenceScreenResId();
if (resId <= 0) {
final PreferenceScreen preferenceScreen = getPreferenceManager().createPreferenceScreen(
@@ -227,16 +211,6 @@
final SettingsMainSwitchBar switchBar = settingsActivity.getSwitchBar();
switchBar.hide();
- // Reshow tooltip when activity recreate, such as rotate device.
- if (mNeedsQSTooltipReshow) {
- view.post(() -> {
- final Activity activity = getActivity();
- if (activity != null && !activity.isFinishing()) {
- showQuickSettingsTooltipIfNeeded();
- }
- });
- }
-
writeConfigDefaultAccessibilityServiceIntoShortcutTargetServiceIfNeeded(getContext());
}
@@ -262,23 +236,9 @@
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
- outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- outState.putInt(KEY_SAVED_QS_TOOLTIP_TYPE, mNeedsQSTooltipType);
- }
- super.onSaveInstanceState(outState);
- }
-
- @Override
public void onDestroyView() {
super.onDestroyView();
removeActionBarToggleSwitch();
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (isTooltipWindowShowing) {
- mTooltipWindow.dismiss();
- }
}
@Override
@@ -314,7 +274,12 @@
/** Returns the accessibility tile component name. */
abstract ComponentName getTileComponentName();
- /** Returns the accessibility tile tooltip content. */
+ /** Returns the accessibility tile component name.
+ *
+ * @deprecated unused, as this class no longer displays tile tooltips.
+ *
+ * (TODO 367414968: finish removal.)*/
+ @Deprecated
abstract CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type);
protected void updateToggleServiceTitle(SettingsMainSwitchPreference switchPreference) {
@@ -332,9 +297,6 @@
}
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- if (enabled) {
- showQuickSettingsTooltipIfNeeded();
- }
}
protected void onInstallSwitchPreferenceToggleSwitch() {
@@ -646,7 +608,6 @@
*/
private void callOnTutorialDialogButtonClicked(DialogInterface dialog, int which) {
dialog.dismiss();
- showQuickSettingsTooltipIfNeeded();
}
protected void updateShortcutPreferenceData() {
@@ -670,8 +631,8 @@
final int shortcutTypes = getUserPreferredShortcutTypes();
mShortcutPreference.setChecked(
- AccessibilityUtil.hasValuesInSettings(getPrefContext(), shortcutTypes,
- mComponentName));
+ ShortcutUtils.isShortcutContained(
+ getPrefContext(), shortcutTypes, mComponentName.flattenToString()));
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
}
@@ -737,26 +698,6 @@
}
}
- /**
- * Shows the quick settings tooltip if the quick settings feature is assigned. The tooltip only
- * shows once.
- *
- * @param type The quick settings tooltip type
- */
- protected void showQuickSettingsTooltipIfNeeded(@QuickSettingsTooltipType int type) {
- mNeedsQSTooltipType = type;
- showQuickSettingsTooltipIfNeeded();
- }
-
- /**
- * @deprecated made obsolete by quick settings rollout.
- *
- * (TODO 367414968: finish removal.)
- */
- @Deprecated
- private void showQuickSettingsTooltipIfNeeded() {
- }
-
/** Returns user visible name of the tile by given {@link ComponentName}. */
protected CharSequence loadTileLabel(Context context, ComponentName componentName) {
final PackageManager packageManager = context.getPackageManager();
diff --git a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
index 16911f6..ff14021 100644
--- a/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleReduceBrightColorsPreferenceFragment.java
@@ -149,9 +149,6 @@
@Override
protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
- if (enabled) {
- showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
- }
logAccessibilityServiceEnabled(mComponentName, enabled);
mColorDisplayManager.setReduceBrightColorsActivated(enabled);
}
diff --git a/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt b/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt
index 70a0033..3265447 100644
--- a/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt
+++ b/src/com/android/settings/accessibility/VibrationMainSwitchPreference.kt
@@ -30,16 +30,16 @@
import com.android.settingslib.metadata.PreferenceLifecycleContext
import com.android.settingslib.metadata.PreferenceLifecycleProvider
import com.android.settingslib.metadata.ReadWritePermit
-import com.android.settingslib.preference.MainSwitchPreferenceBinding
-/**
- * Accessibility settings for vibration.
- */
+/** Accessibility settings for vibration. */
// LINT.IfChange
-class VibrationMainSwitchPreference : MainSwitchPreference(
- key = Settings.System.VIBRATE_ON,
- title = R.string.accessibility_vibration_primary_switch_title,
-), PreferenceLifecycleProvider, OnCheckedChangeListener {
+class VibrationMainSwitchPreference :
+ MainSwitchPreference(
+ key = Settings.System.VIBRATE_ON,
+ title = R.string.accessibility_vibration_primary_switch_title,
+ ),
+ PreferenceLifecycleProvider,
+ OnCheckedChangeListener {
override val keywords: Int
get() = R.string.keywords_accessibility_vibration_primary_switch
@@ -48,20 +48,26 @@
override fun storage(context: Context): KeyValueStore =
VibrationMainSwitchToggleStorage(SettingsSystemStore.get(context))
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override fun onResume(context: PreferenceLifecycleContext) {
vibrator = context.getSystemService(Vibrator::class.java)
- context.findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
+ context
+ .findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
?.addOnSwitchChangeListener(this)
}
override fun onPause(context: PreferenceLifecycleContext) {
- context.findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
+ context
+ .findPreference<com.android.settingslib.widget.MainSwitchPreference>(key)
?.removeOnSwitchChangeListener(this)
}
@@ -69,16 +75,16 @@
if (isChecked) {
// Play a haptic as preview for the main toggle only when touch feedback is enabled.
VibrationPreferenceConfig.playVibrationPreview(
- vibrator, VibrationAttributes.USAGE_TOUCH
+ vibrator,
+ VibrationAttributes.USAGE_TOUCH,
)
}
}
/** Provides SettingsStore for vibration main switch with custom default value. */
@Suppress("UNCHECKED_CAST")
- private class VibrationMainSwitchToggleStorage(
- private val settingsStore: SettingsStore,
- ) : KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
+ private class VibrationMainSwitchToggleStorage(private val settingsStore: SettingsStore) :
+ KeyedObservableDelegate<String>(settingsStore), KeyValueStore {
override fun contains(key: String) = settingsStore.contains(key)
diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
index db239aa..40626b7 100644
--- a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
+++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java
@@ -72,7 +72,6 @@
"enable_app_compat_user_aspect_ratio_fullscreen";
private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true;
- final boolean mIsUserMinAspectRatioAppDefaultFlagEnabled = Flags.userMinAspectRatioAppDefault();
private final boolean mIgnoreActivityOrientationRequest;
private final Context mContext;
@@ -252,8 +251,7 @@
boolean isOverrideToFullscreenEnabled(String pkgName, int userId) {
Boolean appAllowsOrientationOverride = readComponentProperty(mContext.getPackageManager(),
pkgName, PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE);
- return mIsUserMinAspectRatioAppDefaultFlagEnabled
- && hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, pkgName)
+ return hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, pkgName)
&& !FALSE.equals(appAllowsOrientationOverride)
&& (isFullscreenCompatChangeEnabled(pkgName, userId)
|| isUniversalResizeable(pkgName, userId));
@@ -326,15 +324,13 @@
throw new RuntimeException("config_userAspectRatioOverrideValues options must have"
+ " USER_MIN_ASPECT_RATIO_UNSET value");
}
- if (mIsUserMinAspectRatioAppDefaultFlagEnabled) {
- userMinAspectRatioMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
- userMinAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET));
- mUserAspectRatioOrder.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
- mUserAspectRatioOrder.get(USER_MIN_ASPECT_RATIO_UNSET));
- if (mUserAspectRatioA11yMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) {
- mUserAspectRatioA11yMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
- mUserAspectRatioA11yMap.get(USER_MIN_ASPECT_RATIO_UNSET));
- }
+ userMinAspectRatioMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
+ userMinAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET));
+ mUserAspectRatioOrder.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
+ mUserAspectRatioOrder.get(USER_MIN_ASPECT_RATIO_UNSET));
+ if (mUserAspectRatioA11yMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) {
+ mUserAspectRatioA11yMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT,
+ mUserAspectRatioA11yMap.get(USER_MIN_ASPECT_RATIO_UNSET));
}
return userMinAspectRatioMap;
}
diff --git a/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java b/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
index f19b68a..6fc7469 100644
--- a/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
+++ b/src/com/android/settings/applications/specialaccess/pictureinpicture/PictureInPictureSettings.java
@@ -43,7 +43,6 @@
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.EmptyTextSettings;
import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.AppPreference;
import java.text.Collator;
import java.util.ArrayList;
@@ -160,7 +159,7 @@
final String packageName = appInfo.packageName;
final CharSequence label = appInfo.loadLabel(mPackageManager);
- final Preference pref = new AppPreference(prefContext);
+ final Preference pref = new Preference(prefContext);
pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, userId));
pref.setTitle(mPackageManager.getUserBadgedLabel(label, user));
pref.setSummary(PictureInPictureDetails.getPreferenceSummary(prefContext,
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index 14f55b8..bd160e1 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -39,6 +39,7 @@
private final AudioManager mAudioManager;
private final LocalBluetoothManager mLocalBtManager;
+ private int mAudioMode;
public AvailableMediaBluetoothDeviceUpdater(
Context context,
@@ -47,21 +48,23 @@
super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mLocalBtManager = Utils.getLocalBtManager(context);
+ mAudioMode = mAudioManager.getMode();
}
@Override
public void onAudioModeChanged() {
+ // TODO: move to background thread
+ mAudioMode = mAudioManager.getMode();
forceUpdate();
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
- final int audioMode = mAudioManager.getMode();
final int currentAudioProfile;
- if (audioMode == AudioManager.MODE_RINGTONE
- || audioMode == AudioManager.MODE_IN_CALL
- || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ if (mAudioMode == AudioManager.MODE_RINGTONE
+ || mAudioMode == AudioManager.MODE_IN_CALL
+ || mAudioMode == AudioManager.MODE_IN_COMMUNICATION) {
// in phone call
currentAudioProfile = BluetoothProfile.HEADSET;
} else {
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index 2107569..7cc874c 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -39,26 +39,29 @@
private static final String PREF_KEY_PREFIX = "connected_bt_";
private final AudioManager mAudioManager;
+ private int mAudioMode;
public ConnectedBluetoothDeviceUpdater(Context context,
DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAudioMode = mAudioManager.getMode();
}
@Override
public void onAudioModeChanged() {
+ // TODO: move to background thread
+ mAudioMode = mAudioManager.getMode();
forceUpdate();
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
- final int audioMode = mAudioManager.getMode();
final int currentAudioProfile;
- if (audioMode == AudioManager.MODE_RINGTONE
- || audioMode == AudioManager.MODE_IN_CALL
- || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ if (mAudioMode == AudioManager.MODE_RINGTONE
+ || mAudioMode == AudioManager.MODE_IN_CALL
+ || mAudioMode == AudioManager.MODE_IN_COMMUNICATION) {
// in phone call
currentAudioProfile = BluetoothProfile.HEADSET;
} else {
diff --git a/src/com/android/settings/connecteddevice/BluetoothPreference.kt b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
index c9b3953..93b2f20 100644
--- a/src/com/android/settings/connecteddevice/BluetoothPreference.kt
+++ b/src/com/android/settings/connecteddevice/BluetoothPreference.kt
@@ -52,10 +52,15 @@
override val restrictionKeys: Array<String>
get() = arrayOf(UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_CONFIG_BLUETOOTH)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) =
when {
isSatelliteOn(context, 3000) ||
(value == true &&
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
index 25a9135..c86222c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
@@ -21,6 +21,8 @@
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.stream.Collectors.toMap;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioContentMetadata;
@@ -48,7 +50,9 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -149,6 +153,16 @@
.toList();
}
+ /** Retrieves a list of all LE broadcast receive states keyed by each active device. */
+ public Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getAllSourcesByDevice() {
+ if (mLeBroadcastAssistant == null) {
+ Log.w(TAG, "getAllSourcesByDevice(): LeBroadcastAssistant is null!");
+ return emptyMap();
+ }
+ return getConnectedBluetoothDevices(mBluetoothManager, /* inSharingOnly= */ true).stream()
+ .collect(toMap(Function.identity(), mLeBroadcastAssistant::getAllSources));
+ }
+
/** Retrieves a list of all LE broadcast receive states from sinks with source present. */
@VisibleForTesting
public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
index f003431..87cea2c 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
@@ -16,7 +16,6 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
-
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -43,13 +42,13 @@
super.onReceiveStateChanged(sink, sourceId, state);
if (AudioStreamsHelper.isConnected(state)) {
- mCategoryController.handleSourceConnected(state);
+ mCategoryController.handleSourceConnected(sink, state);
} else if (AudioStreamsHelper.isBadCode(state)) {
mCategoryController.handleSourceConnectBadCode(state);
} else if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)
&& AudioStreamsHelper.hasSourcePresent(state)) {
// Keep this check as the last, source might also present in above states
- mCategoryController.handleSourcePresent(state);
+ mCategoryController.handleSourcePresent(sink, state);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
index f0a0c5b..6831c5a 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -17,10 +17,12 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toMap;
import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
@@ -49,6 +51,8 @@
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -391,34 +395,19 @@
// Expect one of the following:
// 1) No preference existed, create new preference with state SOURCE_ADDED
// 2) Any other state, move to SOURCE_ADDED
- void handleSourceConnected(BluetoothLeBroadcastReceiveState receiveState) {
+ void handleSourceConnected(
+ BluetoothDevice device, BluetoothLeBroadcastReceiveState receiveState) {
if (DEBUG) {
Log.d(TAG, "handleSourceConnected()");
}
if (!AudioStreamsHelper.isConnected(receiveState)) {
return;
}
-
var broadcastIdConnected = receiveState.getBroadcastId();
- if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
- // mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
- // connected source receiveState.
- if (DEBUG) {
- Log.d(
- TAG,
- "handleSourceConnected() : processing mSourceFromQrCode with broadcastId"
- + " unset");
- }
- boolean updated =
- maybeUpdateId(
- AudioStreamsHelper.getBroadcastName(receiveState),
- receiveState.getBroadcastId());
- if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
- var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
- mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
- }
- }
-
+ Optional<BluetoothLeBroadcastMetadata> metadata =
+ getMetadataMatchingByBroadcastId(
+ device, receiveState.getSourceId(), broadcastIdConnected);
+ handleQrCodeWithUnsetBroadcastIdIfNeeded(metadata, receiveState);
mBroadcastIdToPreferenceMap.compute(
broadcastIdConnected,
(k, existingPreference) -> {
@@ -428,7 +417,12 @@
// we retrieves the connected source during onStart() from
// AudioStreamsHelper#getAllConnectedSources() even before the source is
// founded by scanning.
- return addNewPreference(receiveState, AudioStreamState.SOURCE_ADDED);
+ return metadata.isPresent()
+ ? addNewPreference(
+ metadata.get(),
+ AudioStreamState.SOURCE_ADDED,
+ SourceOriginForLogging.UNKNOWN)
+ : addNewPreference(receiveState, AudioStreamState.SOURCE_ADDED);
}
if (existingPreference.getAudioStreamState() == AudioStreamState.WAIT_FOR_SYNC
&& existingPreference.getAudioStreamBroadcastId() == UNSET_BROADCAST_ID
@@ -473,7 +467,8 @@
// Find preference by receiveState and decide next state.
// Expect one preference existed, move to SOURCE_PRESENT
- void handleSourcePresent(BluetoothLeBroadcastReceiveState receiveState) {
+ void handleSourcePresent(
+ BluetoothDevice device, BluetoothLeBroadcastReceiveState receiveState) {
if (DEBUG) {
Log.d(TAG, "handleSourcePresent()");
}
@@ -482,25 +477,10 @@
}
var broadcastIdConnected = receiveState.getBroadcastId();
- if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
- // mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
- // connected source receiveState.
- if (DEBUG) {
- Log.d(
- TAG,
- "handleSourcePresent() : processing mSourceFromQrCode with broadcastId"
- + " unset");
- }
- boolean updated =
- maybeUpdateId(
- AudioStreamsHelper.getBroadcastName(receiveState),
- receiveState.getBroadcastId());
- if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
- var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
- mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
- }
- }
-
+ Optional<BluetoothLeBroadcastMetadata> metadata =
+ getMetadataMatchingByBroadcastId(
+ device, receiveState.getSourceId(), broadcastIdConnected);
+ handleQrCodeWithUnsetBroadcastIdIfNeeded(metadata, receiveState);
mBroadcastIdToPreferenceMap.compute(
broadcastIdConnected,
(k, existingPreference) -> {
@@ -511,7 +491,12 @@
// we retrieves the connected source during onStart() from
// AudioStreamsHelper#getAllPresentSources() even before the source is
// founded by scanning.
- return addNewPreference(receiveState, AudioStreamState.SOURCE_PRESENT);
+ return metadata.isPresent()
+ ? addNewPreference(
+ metadata.get(),
+ AudioStreamState.SOURCE_PRESENT,
+ SourceOriginForLogging.UNKNOWN)
+ : addNewPreference(receiveState, AudioStreamState.SOURCE_PRESENT);
}
if (existingPreference.getAudioStreamState() == AudioStreamState.WAIT_FOR_SYNC
&& existingPreference.getAudioStreamBroadcastId() == UNSET_BROADCAST_ID
@@ -598,28 +583,85 @@
// Handle QR code scan, display currently connected streams then start scanning
// sequentially
handleSourceFromQrCodeIfExists();
+ Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources =
+ mAudioStreamsHelper.getAllSourcesByDevice();
+ Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> connectedSources =
+ getConnectedSources(sources);
if (isAudioSharingHysteresisModeFixAvailable(mContext)) {
// With hysteresis mode, we prioritize showing connected sources first.
// If no connected sources are found, we then show present sources.
- List<BluetoothLeBroadcastReceiveState> sources =
- mAudioStreamsHelper.getAllConnectedSources();
- if (!sources.isEmpty()) {
- sources.forEach(this::handleSourceConnected);
+ if (!connectedSources.isEmpty()) {
+ connectedSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourceConnected(device, state)));
} else {
- mAudioStreamsHelper
- .getAllPresentSources()
- .forEach(this::handleSourcePresent);
+ Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>>
+ presentSources = getPresentSources(sources);
+ presentSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourcePresent(device, state)));
}
} else {
- mAudioStreamsHelper
- .getAllConnectedSources()
- .forEach(this::handleSourceConnected);
+ connectedSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourceConnected(device, state)));
}
mLeBroadcastAssistant.startSearchingForSources(emptyList());
mMediaControlHelper.start();
});
}
+ private Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getConnectedSources(
+ Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources) {
+ return sources.entrySet().stream()
+ .filter(
+ entry ->
+ entry.getValue().stream().anyMatch(AudioStreamsHelper::isConnected))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getPresentSources(
+ Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources) {
+ return sources.entrySet().stream()
+ .filter(
+ entry ->
+ entry.getValue().stream()
+ .anyMatch(AudioStreamsHelper::hasSourcePresent))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Optional<BluetoothLeBroadcastMetadata> getMetadataMatchingByBroadcastId(
+ BluetoothDevice device, int sourceId, int broadcastId) {
+ return Optional.ofNullable(
+ mLeBroadcastAssistant != null
+ ? mLeBroadcastAssistant.getSourceMetadata(device, sourceId)
+ : null)
+ .filter(m -> m.getBroadcastId() == broadcastId);
+ }
+
+ private void handleQrCodeWithUnsetBroadcastIdIfNeeded(
+ Optional<BluetoothLeBroadcastMetadata> metadata,
+ BluetoothLeBroadcastReceiveState receiveState) {
+ if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
+ if (DEBUG) {
+ Log.d(TAG, "Processing mSourceFromQrCode with unset broadcastId");
+ }
+ boolean updated =
+ maybeUpdateId(
+ metadata.isPresent()
+ ? AudioStreamsHelper.getBroadcastName(metadata.get())
+ : AudioStreamsHelper.getBroadcastName(receiveState),
+ receiveState.getBroadcastId());
+ if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
+ var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
+ mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
+ }
+ }
+ }
+
private void stopScanning() {
if (mLeBroadcastAssistant == null) {
Log.w(TAG, "stopScanning(): LeBroadcastAssistant is null!");
diff --git a/src/com/android/settings/connecteddevice/display/DisplayTopology.kt b/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
index 162d9d2..9cac772 100644
--- a/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
+++ b/src/com/android/settings/connecteddevice/display/DisplayTopology.kt
@@ -16,14 +16,32 @@
package com.android.settings.connecteddevice.display
+import android.app.WallpaperManager
import com.android.settings.R
import android.content.Context
+import android.graphics.Color
import android.graphics.Point
import android.graphics.PointF
import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ColorDrawable
+import android.hardware.display.DisplayManager
+import android.hardware.display.DisplayTopology
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_BOTTOM
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
+import android.util.Log
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.widget.Button
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.annotation.VisibleForTesting
import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
import java.util.Locale
@@ -41,8 +59,20 @@
* left corner of the pane. It uses a scale optimized for showing all displays with minimal or no
* scrolling. The display coordinates are floating point and the origin can be in any position. In
* practice the origin will be the upper-left coordinate of the primary display.
+ *
+ * @param paneWidth width of the pane in view coordinates
+ * @param minEdgeLength the smallest length permitted of a display block. This should be set based
+ * on accessibility requirements, but also accounting for padding that appears
+ * around each button.
+ * @param maxBlockRatio the highest allowed ratio of block size to display size. For instance, a
+ * value of 0.05 means the block will at most be 1/20 the size of the display
+ * it represents. This limit may be breached to account for minEdgeLength,
+ * which is considered an a11y requirement.
+ * @param displaysPos the absolute topology coordinates for each display in the topology.
*/
-class TopologyScale(paneWidth : Int, displaysPos : Collection<RectF>) {
+class TopologyScale(
+ paneWidth : Int, minEdgeLength : Int, maxBlockRatio : Float,
+ displaysPos : Collection<RectF>) {
/** Scale of block sizes to real-world display sizes. Should be less than 1. */
val blockRatio : Float
@@ -69,16 +99,14 @@
biggestDisplayHeight = max(biggestDisplayHeight, pos.height())
}
- // Set height according to the width and the aspect ratio of the display bounds.
- // 0.05 is a reasonable limit to the size of display blocks. It appears to match the
- // ratio used in the ChromeOS topology editor. It prevents blocks from being too large,
- // which would make dragging and dropping awkward.
- val rawBlockRatio = min(0.05, paneWidth.toDouble() * 0.6 / displayBounds.width())
+ // Set height according to the width and the aspect ratio of the display bounds limitted by
+ // maxBlockRatio. It prevents blocks from being too large, which would make dragging and
+ // dropping awkward.
+ val rawBlockRatio = min(maxBlockRatio, paneWidth.toFloat() * 0.6f / displayBounds.width())
// If the `ratio` is set too low because one of the displays will have an edge less than
- // 48dp long, increase it such that the smallest edge is that long. This may override the
- // 0.05 limit since it is more important than it.
- blockRatio = max(48.0 / smallestDisplayDim, rawBlockRatio).toFloat()
+ // minEdgeLength(dp) long, increase it such that the smallest edge is that long.
+ blockRatio = max(minEdgeLength.toFloat() / smallestDisplayDim, rawBlockRatio).toFloat()
// Essentially, we just set the pane height based on the pre-determined pane width and the
// aspect ratio of the display bounds. But we may need to increase it slightly to achieve
@@ -99,9 +127,9 @@
// such that the display bounds rect is centered in the pane.
// It is unlikely that either of these coordinates will be negative since blockRatio has
// been chosen to allow 20% padding around each side of the display blocks. However, the
- // a11y requirement applied above (48.0 / smallestDisplayDim) may cause the blocks to not
- // fit. This should be rare in practice, and can be worked around by moving the settings UI
- // to a larger display.
+ // a11y requirement applied above (minEdgeLength / smallestDisplayDim) may cause the blocks
+ // to not fit. This should be rare in practice, and can be worked around by moving the
+ // settings UI to a larger display.
val blockMostLeft = (paneWidth - displayBounds.width() * blockRatio) / 2
val blockMostTop = (paneHeight - displayBounds.height() * blockRatio) / 2
@@ -133,11 +161,27 @@
const val PREFERENCE_KEY = "display_topology_preference"
+/** dp of padding on each side of a display block. */
+const val BLOCK_PADDING = 2
+
/**
* DisplayTopologyPreference allows the user to change the display topology
* when there is one or more extended display attached.
*/
-class DisplayTopologyPreference(context : Context) : Preference(context) {
+class DisplayTopologyPreference(context : Context)
+ : Preference(context), ViewTreeObserver.OnGlobalLayoutListener {
+ @VisibleForTesting lateinit var mPaneContent : FrameLayout
+ @VisibleForTesting lateinit var mPaneHolder : FrameLayout
+ @VisibleForTesting lateinit var mTopologyHint : TextView
+
+ @VisibleForTesting var injector : Injector
+
+ /**
+ * This is needed to prevent a repopulation of the pane causing another
+ * relayout and vice-versa ad infinitum.
+ */
+ private var mPaneNeedsRefresh = false
+
init {
layoutResource = R.layout.display_topology_preference
@@ -145,5 +189,108 @@
isSelectable = false
key = PREFERENCE_KEY
+
+ injector = Injector()
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+
+ val newPane = holder.findViewById(R.id.display_topology_pane_content) as FrameLayout
+ if (this::mPaneContent.isInitialized) {
+ if (newPane == mPaneContent) {
+ return
+ }
+ mPaneContent.viewTreeObserver.removeOnGlobalLayoutListener(this)
+ }
+ mPaneContent = newPane
+ mPaneHolder = holder.itemView as FrameLayout
+ mTopologyHint = holder.findViewById(R.id.topology_hint) as TextView
+ mPaneContent.viewTreeObserver.addOnGlobalLayoutListener(this)
+ }
+
+ override fun onAttached() {
+ // We don't know if topology changes happened when we were detached, as it is impossible to
+ // listen at that time (we must remove listeners when detaching). Setting this flag makes
+ // the following onGlobalLayout call refresh the pane.
+ mPaneNeedsRefresh = true
+ }
+
+ override fun onGlobalLayout() {
+ if (mPaneNeedsRefresh) {
+ mPaneNeedsRefresh = false
+ refreshPane()
+ }
+ }
+
+ open class Injector {
+ open fun displayTopology(context : Context) : DisplayTopology? {
+ val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ return displayManager.displayTopology
+ }
+
+ open fun wallpaper(context : Context) : Drawable {
+ return WallpaperManager.getInstance(context).drawable ?: ColorDrawable(Color.BLACK)
+ }
+ }
+
+ private fun calcAbsRects(
+ dest : MutableMap<Int, RectF>, n : DisplayTopology.TreeNode, x : Float, y : Float) {
+ dest.put(n.displayId, RectF(x, y, x + n.width, y + n.height))
+
+ for (c in n.children) {
+ val (xoff, yoff) = when (c.position) {
+ POSITION_LEFT -> Pair(-c.width, +c.offset)
+ POSITION_RIGHT -> Pair(+n.width, +c.offset)
+ POSITION_TOP -> Pair(+c.offset, -c.height)
+ POSITION_BOTTOM -> Pair(+c.offset, +n.height)
+ else -> throw IllegalStateException("invalid position for display: ${c}")
+ }
+ calcAbsRects(dest, c, x + xoff, y + yoff)
+ }
+ }
+
+ private fun refreshPane() {
+ mPaneContent.removeAllViews()
+
+ val root = injector.displayTopology(context)?.root
+ if (root == null) {
+ // This occurs when no topology is active.
+ // TODO(b/352648432): show main display or mirrored displays rather than an empty pane.
+ mTopologyHint.text = ""
+ return
+ }
+ mTopologyHint.text = context.getString(R.string.external_display_topology_hint)
+
+ val blocksPos = buildMap { calcAbsRects(this, root, x = 0f, y = 0f) }
+
+ val scaling = TopologyScale(
+ mPaneContent.width, minEdgeLength = 60, maxBlockRatio = 0.12f, blocksPos.values)
+ mPaneHolder.layoutParams.let {
+ if (it.height != scaling.paneHeight) {
+ it.height = scaling.paneHeight
+ mPaneHolder.layoutParams = it
+ }
+ }
+ val wallpaper = injector.wallpaper(context)
+ blocksPos.values.forEach { p ->
+ Button(context).apply {
+ isScrollContainer = false
+ isVerticalScrollBarEnabled = false
+ isHorizontalScrollBarEnabled = false
+ background = wallpaper
+ val topLeft = scaling.displayToPaneCoor(PointF(p.left, p.top))
+ val bottomRight = scaling.displayToPaneCoor(PointF(p.right, p.bottom))
+
+ mPaneContent.addView(this)
+
+ val layout = layoutParams
+ layout.width = bottomRight.x - topLeft.x - BLOCK_PADDING * 2
+ layout.height = bottomRight.y - topLeft.y - BLOCK_PADDING * 2
+ layoutParams = layout
+ x = (topLeft.x + BLOCK_PADDING).toFloat()
+ y = (topLeft.y + BLOCK_PADDING).toFloat()
+ }
+ }
}
}
diff --git a/src/com/android/settings/core/gateway/SettingsGateway.java b/src/com/android/settings/core/gateway/SettingsGateway.java
index 8541c7a..eeecdd5 100644
--- a/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -130,7 +130,6 @@
import com.android.settings.gestures.SwipeToNotificationSettings;
import com.android.settings.gestures.SystemNavigationGestureSettings;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
-import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
import com.android.settings.inputmethod.KeyboardSettings;
import com.android.settings.inputmethod.ModifierKeysSettings;
import com.android.settings.inputmethod.NewKeyboardLayoutEnabledLocalesFragment;
@@ -156,6 +155,7 @@
import com.android.settings.network.tether.TetherSettings;
import com.android.settings.nfc.PaymentSettings;
import com.android.settings.notification.ConfigureNotificationSettings;
+import com.android.settings.notification.LockScreenNotificationsPreferencePageFragment;
import com.android.settings.notification.NotificationAccessSettings;
import com.android.settings.notification.NotificationAssistantPicker;
import com.android.settings.notification.PoliteNotificationsPreferenceFragment;
@@ -309,7 +309,6 @@
PrintJobSettingsFragment.class.getName(),
TrustedCredentialsSettings.class.getName(),
PaymentSettings.class.getName(),
- KeyboardLayoutPickerFragment.class.getName(),
PhysicalKeyboardFragment.class.getName(),
ZenModeSettings.class.getName(),
SoundSettings.class.getName(),
@@ -365,6 +364,7 @@
EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(),
LockscreenDashboardFragment.class.getName(),
+ LockScreenNotificationsPreferencePageFragment.class.getName(),
MemtagPage.class.getName(),
BluetoothDeviceDetailsFragment.class.getName(),
BluetoothBroadcastDialog.class.getName(),
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 8480c5c..d8e238c 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -18,6 +18,7 @@
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUid;
import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUidList;
+import static com.android.settings.spa.app.appinfo.AppInfoSettingsProvider.startAppInfoSettings;
import android.app.Activity;
import android.app.settings.SettingsEnums;
@@ -45,13 +46,14 @@
import com.android.settings.datausage.lib.NetworkTemplates;
import com.android.settings.fuelgauge.datasaver.DynamicDenylistManager;
import com.android.settings.network.SubscriptionUtil;
-import com.android.settings.widget.EntityHeaderController;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
+import com.android.settingslib.widget.IntroPreference;
import kotlin.Unit;
@@ -65,6 +67,8 @@
private static final String TAG = "AppDataUsage";
static final String ARG_APP_ITEM = "app_item";
+ @VisibleForTesting
+ static final String ARG_APP_HEADER = "app_header";
static final String ARG_NETWORK_TEMPLATE = "network_template";
static final String ARG_NETWORK_CYCLES = "network_cycles";
static final String ARG_SELECTED_CYCLE = "selected_cycle";
@@ -176,7 +180,7 @@
removePreference(KEY_RESTRICT_BACKGROUND);
}
- addEntityHeader();
+ setupIntroPreference();
}
@Override
@@ -320,32 +324,32 @@
}
@VisibleForTesting
- void addEntityHeader() {
- String pkg = mPackages.size() != 0 ? mPackages.valueAt(0) : null;
- int uid = 0;
- if (pkg != null) {
+ void setupIntroPreference() {
+ final Preference pref = getPreferenceScreen().findPreference(ARG_APP_HEADER);
+ if (pref != null) {
+ pref.setIcon(mIcon);
+ pref.setTitle(mLabel);
+ pref.setSelectable(true);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ if (!(preference instanceof IntroPreference)) return false;
+
+ String pkg = !mPackages.isEmpty() ? mPackages.valueAt(0) : null;
+ if (mAppItem.key > 0 && pkg != null) {
try {
- uid = mPackageManager.getPackageUidAsUser(pkg,
+ int uid = mPackageManager.getPackageUidAsUser(pkg,
UserHandle.getUserId(mAppItem.key));
+ startAppInfoSettings(pkg, uid, this, 0 /* request */,
+ FeatureFactory.getFeatureFactory().getMetricsFeatureProvider()
+ .getMetricsCategory(this));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Skipping UID because cannot find package " + pkg);
}
}
-
- final boolean showInfoButton = mAppItem.key > 0;
-
- final Activity activity = getActivity();
- final Preference pref = EntityHeaderController
- .newInstance(activity, this, null /* header */)
- .setUid(uid)
- .setHasAppInfoLink(showInfoButton)
- .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
- EntityHeaderController.ActionType.ACTION_NONE)
- .setIcon(mIcon)
- .setLabel(mLabel)
- .setPackageName(pkg)
- .done(getPrefContext());
- getPreferenceScreen().addPreference(pref);
+ return true;
}
@Override
diff --git a/src/com/android/settings/datausage/AppDataUsageSummaryController.kt b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt
index 233e107..fb7101d 100644
--- a/src/com/android/settings/datausage/AppDataUsageSummaryController.kt
+++ b/src/com/android/settings/datausage/AppDataUsageSummaryController.kt
@@ -17,7 +17,6 @@
package com.android.settings.datausage
import android.content.Context
-import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.res.stringResource
@@ -28,6 +27,7 @@
import com.android.settings.spa.preference.ComposePreferenceController
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
@@ -60,7 +60,7 @@
@Composable
override fun Content() {
- Column {
+ Category {
val totalUsage by totalUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
val foregroundUsage by foregroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
diff --git a/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt b/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt
index 23cfadc..79d3dc3 100644
--- a/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt
+++ b/src/com/android/settings/datausage/DataSaverMainSwitchPreference.kt
@@ -36,11 +36,15 @@
override fun storage(context: Context) = createDataStore(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/datausage/SpinnerPreference.java b/src/com/android/settings/datausage/SpinnerPreference.java
index febdead..a1b0f90 100644
--- a/src/com/android/settings/datausage/SpinnerPreference.java
+++ b/src/com/android/settings/datausage/SpinnerPreference.java
@@ -25,8 +25,10 @@
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
+import com.android.settingslib.widget.GroupSectionDividerMixin;
-public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
+public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface,
+ GroupSectionDividerMixin {
private CycleAdapter mAdapter;
@Nullable
diff --git a/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java b/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java
deleted file mode 100644
index 44e7cc6..0000000
--- a/src/com/android/settings/datetime/AutoTimeFormatPreferenceController.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.datetime;
-
-import android.content.Context;
-import android.provider.Settings;
-import android.provider.Settings.System;
-import android.text.format.DateFormat;
-
-import com.android.settings.R;
-import com.android.settings.core.TogglePreferenceController;
-
-import java.util.Locale;
-
-public class AutoTimeFormatPreferenceController extends TogglePreferenceController {
-
- public AutoTimeFormatPreferenceController(Context context, String preferenceKey) {
- super(context, preferenceKey);
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean isChecked() {
- return isAutoTimeFormatSelection(mContext);
- }
-
- @Override
- public boolean setChecked(boolean isChecked) {
- Boolean is24Hour;
- if (isChecked) {
- is24Hour = null;
- } else {
- is24Hour = is24HourLocale(mContext.getResources().getConfiguration().locale);
- }
- TimeFormatPreferenceController.update24HourFormat(mContext, is24Hour);
- return true;
- }
-
- @Override
- public int getSliceHighlightMenuRes() {
- return R.string.menu_key_system;
- }
-
- boolean is24HourLocale(Locale locale) {
- return DateFormat.is24HourLocale(locale);
- }
-
- /**
- * Returns if the system is currently configured to pick the time format automatically based on
- * the locale.
- */
- static boolean isAutoTimeFormatSelection(Context context) {
- return Settings.System.getString(context.getContentResolver(), System.TIME_12_24) == null;
- }
-}
diff --git a/src/com/android/settings/datetime/AutoTimePreferenceController.java b/src/com/android/settings/datetime/AutoTimePreferenceController.java
index 5b17e7e..cc005d7 100644
--- a/src/com/android/settings/datetime/AutoTimePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimePreferenceController.java
@@ -26,6 +26,7 @@
import android.app.time.TimeConfiguration;
import android.app.time.TimeManager;
import android.content.Context;
+import android.util.Log;
import androidx.preference.Preference;
@@ -35,6 +36,8 @@
public class AutoTimePreferenceController extends TogglePreferenceController {
+ private static final String TAG = "AutoTimePreferenceController";
+
private UpdateTimeAndDateCallback mCallback;
private final TimeManager mTimeManager;
@@ -74,7 +77,8 @@
// arbitrary.
return AVAILABLE;
default:
- throw new IllegalStateException("Unknown capability=" + capability);
+ Log.e(TAG, "Unknown capability=" + capability);
+ return UNSUPPORTED_ON_DEVICE;
}
}
diff --git a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
index 3d8f801..f60a5cc 100644
--- a/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
+++ b/src/com/android/settings/datetime/AutoTimeZonePreferenceController.java
@@ -26,16 +26,18 @@
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.content.Context;
+import android.util.Log;
import androidx.preference.Preference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
-import com.android.settings.flags.Flags;
public class AutoTimeZonePreferenceController extends TogglePreferenceController {
+ private static final String TAG = "AutoTimeZonePreferenceController";
+
private boolean mIsFromSUW;
private UpdateTimeAndDateCallback mCallback;
private final TimeManager mTimeManager;
@@ -93,7 +95,8 @@
// arbitrary.
return AVAILABLE;
default:
- throw new IllegalStateException("Unknown capability=" + capability);
+ Log.e(TAG, "Unknown capability=" + capability);
+ return UNSUPPORTED_ON_DEVICE;
}
}
@@ -107,19 +110,17 @@
TimeZoneConfiguration.Builder configuration = new TimeZoneConfiguration.Builder()
.setAutoDetectionEnabled(isChecked);
- if (Flags.revampToggles()) {
- // "Use location for time zone" is only used if "Automatic time zone" is enabled. If
- // the user toggles off automatic time zone, set the toggle off and disable the toggle.
- int geoDetectionCapability = mTimeManager
- .getTimeZoneCapabilitiesAndConfig()
- .getCapabilities()
- .getConfigureGeoDetectionEnabledCapability();
+ // "Use location for time zone" is only used if "Automatic time zone" is enabled. If
+ // the user toggles off automatic time zone, set the toggle off and disable the toggle.
+ int geoDetectionCapability = mTimeManager
+ .getTimeZoneCapabilitiesAndConfig()
+ .getCapabilities()
+ .getConfigureGeoDetectionEnabledCapability();
- if (!isChecked
- && (geoDetectionCapability == CAPABILITY_NOT_APPLICABLE
- || geoDetectionCapability == CAPABILITY_POSSESSED)) {
- configuration.setGeoDetectionEnabled(false);
- }
+ if (!isChecked
+ && (geoDetectionCapability == CAPABILITY_NOT_APPLICABLE
+ || geoDetectionCapability == CAPABILITY_POSSESSED)) {
+ configuration.setGeoDetectionEnabled(false);
}
boolean result = mTimeManager.updateTimeZoneConfiguration(configuration.build());
diff --git a/src/com/android/settings/datetime/DateTimeSettings.java b/src/com/android/settings/datetime/DateTimeSettings.java
index e5c13bf..f3c11d4 100644
--- a/src/com/android/settings/datetime/DateTimeSettings.java
+++ b/src/com/android/settings/datetime/DateTimeSettings.java
@@ -23,7 +23,6 @@
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.flags.Flags;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -50,9 +49,6 @@
@Override
protected int getPreferenceScreenResId() {
- if (Flags.revampToggles()) {
- return R.xml.date_time_prefs_revamped;
- }
return R.xml.date_time_prefs;
}
@@ -123,6 +119,5 @@
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(
- Flags.revampToggles() ? R.xml.date_time_prefs_revamped : R.xml.date_time_prefs);
+ new BaseSearchIndexProvider(R.xml.date_time_prefs);
}
diff --git a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
index 52d49ac..05f5af6 100644
--- a/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
+++ b/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceController.java
@@ -25,6 +25,7 @@
import android.app.time.TimeZoneCapabilitiesAndConfig;
import android.app.time.TimeZoneConfiguration;
import android.content.Context;
+import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -32,7 +33,6 @@
import com.android.settings.R;
import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.TogglePreferenceController;
-import com.android.settings.flags.Flags;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -68,7 +68,7 @@
// forceRefresh set to true as the location toggle may have been turned off by switching off
// automatic time zone
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
- getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/ Flags.revampToggles());
+ getTimeZoneCapabilitiesAndConfig(/*forceRefresh=*/ true);
TimeZoneConfiguration configuration = capabilitiesAndConfig.getConfiguration();
return configuration.isGeoDetectionEnabled();
}
@@ -137,13 +137,10 @@
if (capability == CAPABILITY_NOT_SUPPORTED || capability == CAPABILITY_NOT_ALLOWED) {
return UNSUPPORTED_ON_DEVICE;
} else if (capability == CAPABILITY_NOT_APPLICABLE || capability == CAPABILITY_POSSESSED) {
- if (Flags.revampToggles()) {
- return isAutoTimeZoneEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
- } else {
- return AVAILABLE;
- }
+ return isAutoTimeZoneEnabled() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
} else {
- throw new IllegalStateException("Unknown capability=" + capability);
+ Log.e(TAG, "Unknown capability=" + capability);
+ return UNSUPPORTED_ON_DEVICE;
}
}
@@ -151,10 +148,8 @@
public void updateState(Preference preference) {
super.updateState(preference);
- if (Flags.revampToggles()) {
- // enable / disable the toggle based on automatic time zone being enabled or not
- preference.setEnabled(isAutoTimeZoneEnabled());
- }
+ // enable / disable the toggle based on automatic time zone being enabled or not
+ preference.setEnabled(isAutoTimeZoneEnabled());
}
@Override
diff --git a/src/com/android/settings/datetime/TimeFormatPreferenceController.java b/src/com/android/settings/datetime/TimeFormatPreferenceController.java
index 2dee76e..c400f4e 100644
--- a/src/com/android/settings/datetime/TimeFormatPreferenceController.java
+++ b/src/com/android/settings/datetime/TimeFormatPreferenceController.java
@@ -25,7 +25,6 @@
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
-import com.android.settings.flags.Flags;
import java.util.Calendar;
import java.util.Date;
@@ -73,11 +72,6 @@
if (mIsFromSUW) {
return DISABLED_DEPENDENT_SETTING;
}
- if (!Flags.revampToggles()) {
- if (AutoTimeFormatPreferenceController.isAutoTimeFormatSelection(mContext)) {
- return DISABLED_DEPENDENT_SETTING;
- }
- }
return AVAILABLE;
}
@@ -120,12 +114,12 @@
return DateFormat.is24HourFormat(mContext);
}
- static void update24HourFormat(Context context, Boolean is24Hour) {
+ private static void update24HourFormat(Context context, Boolean is24Hour) {
set24Hour(context, is24Hour);
timeUpdated(context, is24Hour);
}
- static void timeUpdated(Context context, Boolean is24Hour) {
+ private static void timeUpdated(Context context, Boolean is24Hour) {
Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
timeChanged.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
int timeFormatPreference;
@@ -139,9 +133,8 @@
context.sendBroadcast(timeChanged);
}
- static void set24Hour(Context context, Boolean is24Hour) {
- String value = is24Hour == null ? null :
- is24Hour ? HOURS_24 : HOURS_12;
+ private static void set24Hour(Context context, boolean is24Hour) {
+ String value = is24Hour ? HOURS_24 : HOURS_12;
Settings.System.putString(context.getContentResolver(),
Settings.System.TIME_12_24, value);
}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 9c13794..605f3bb 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -837,6 +837,7 @@
controllers.add(new ForceEnableNotesRolePreferenceController(context));
controllers.add(new GrammaticalGenderPreferenceController(context));
controllers.add(new SensitiveContentProtectionPreferenceController(context));
+ controllers.add(new ShadeDisplayAwarenessPreferenceController(context));
return controllers;
}
diff --git a/src/com/android/settings/development/ShadeDisplayAwarenessPreferenceController.java b/src/com/android/settings/development/ShadeDisplayAwarenessPreferenceController.java
new file mode 100644
index 0000000..0551112
--- /dev/null
+++ b/src/com/android/settings/development/ShadeDisplayAwarenessPreferenceController.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 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 android.content.Context;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+
+import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settingslib.R;
+import com.android.settingslib.development.DeveloperOptionsPreferenceController;
+
+public class ShadeDisplayAwarenessPreferenceController extends DeveloperOptionsPreferenceController
+ implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin,
+ RebootConfirmationDialogHost {
+
+ private static final int SHADE_DISPLAY_AWARENESS_DEFAULT = 0;
+ private static final String SHADE_DISPLAY_AWARENESS_KEY = "shade_display_awareness";
+
+ private final String[] mListValues;
+ private final String[] mListSummaries;
+
+ public ShadeDisplayAwarenessPreferenceController(Context context) {
+ super(context);
+
+ mListValues = mContext.getResources().getStringArray(
+ R.array.shade_display_awareness_values);
+ mListSummaries = mContext.getResources().getStringArray(
+ R.array.shade_display_awareness_summaries);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return SHADE_DISPLAY_AWARENESS_KEY;
+ }
+
+ @Override
+ public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS, newValue.toString());
+ updateShadeDisplayAwareness((ListPreference) mPreference);
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ updateShadeDisplayAwareness((ListPreference) mPreference);
+ }
+
+ private void updateShadeDisplayAwareness(ListPreference preference) {
+ String currentValue = Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS);
+ int index = SHADE_DISPLAY_AWARENESS_DEFAULT; // Defaults to value is device-display (0)
+ for (int i = 0; i < mListValues.length; i++) {
+ if (TextUtils.equals(currentValue, mListValues[i])) {
+ index = i;
+ break;
+ }
+ }
+ preference.setValue(mListValues[index]);
+ preference.setSummary(mListSummaries[index]);
+ }
+
+ @Override
+ protected void onDeveloperOptionsSwitchDisabled() {
+ super.onDeveloperOptionsSwitchDisabled();
+ Settings.Global.putString(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS,
+ mListValues[SHADE_DISPLAY_AWARENESS_DEFAULT]);
+ }
+}
diff --git a/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java b/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java
index 04dfc79..ac42ee3 100644
--- a/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java
+++ b/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java
@@ -85,6 +85,8 @@
if (bluetoothA2dp == null) {
return;
}
+ // update the cache of a2dp config(mBluetoothA2dpConfigStore) before writing config.
+ initConfigStore();
writeConfigurationValues(index);
final BluetoothCodecConfig codecConfig = mBluetoothA2dpConfigStore.createCodecConfig();
BluetoothDevice activeDevice = getA2dpActiveDevice();
diff --git a/src/com/android/settings/display/AdaptiveSleepPreference.kt b/src/com/android/settings/display/AdaptiveSleepPreference.kt
index a160001..7cc320d 100644
--- a/src/com/android/settings/display/AdaptiveSleepPreference.kt
+++ b/src/com/android/settings/display/AdaptiveSleepPreference.kt
@@ -77,11 +77,15 @@
override fun storage(context: Context): KeyValueStore = Storage(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt
index e50b00b..7a6df8e 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreference.kt
@@ -71,11 +71,15 @@
override fun storage(context: Context): KeyValueStore = Storage(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java b/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java
index 7afe3ea..d9979a9 100644
--- a/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java
+++ b/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizard.java
@@ -19,8 +19,12 @@
import android.content.Context;
import androidx.annotation.NonNull;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.settings.accessibility.Flags;
+import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
/**
* The top-level preference controller that updates the adaptive brightness in the SetupWizard.
@@ -28,15 +32,35 @@
public class AutoBrightnessPreferenceControllerForSetupWizard
extends AutoBrightnessPreferenceController {
+ private RestrictedPreferenceHelper mRestrictedPreferenceHelper;
+
public AutoBrightnessPreferenceControllerForSetupWizard(@NonNull Context context,
@NonNull String key) {
super(context, key);
}
+ private boolean isRestricted() {
+ if (mRestrictedPreferenceHelper == null) {
+ return false;
+ }
+ return mRestrictedPreferenceHelper.isDisabledByAdmin()
+ || mRestrictedPreferenceHelper.isDisabledByEcm();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ Preference preference = screen.findPreference(getPreferenceKey());
+ if (preference instanceof RestrictedPreferenceHelperProvider helperProvider) {
+ mRestrictedPreferenceHelper = helperProvider.getRestrictedPreferenceHelper();
+ preference.setVisible(!isRestricted());
+ }
+ }
+
@Override
@AvailabilityStatus
public int getAvailabilityStatus() {
- if (!Flags.addBrightnessSettingsInSuw()) {
+ if (!Flags.addBrightnessSettingsInSuw() || isRestricted()) {
return CONDITIONALLY_UNAVAILABLE;
}
return super.getAvailabilityStatus();
diff --git a/src/com/android/settings/display/AutoBrightnessScreen.kt b/src/com/android/settings/display/AutoBrightnessScreen.kt
index 32d70ad..319d95b 100644
--- a/src/com/android/settings/display/AutoBrightnessScreen.kt
+++ b/src/com/android/settings/display/AutoBrightnessScreen.kt
@@ -65,11 +65,15 @@
override fun storage(context: Context): KeyValueStore =
AutoBrightnessDataStore(SettingsSystemStore.get(context))
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt
index 25623b3..fb2b74c 100644
--- a/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt
+++ b/src/com/android/settings/display/BatteryPercentageSwitchPreference.kt
@@ -49,11 +49,15 @@
com.android.internal.R.bool.config_battery_percentage_setting_available
)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/display/BrightnessLevelPreference.kt b/src/com/android/settings/display/BrightnessLevelPreference.kt
index 5e2f6f2..ca54d78 100644
--- a/src/com/android/settings/display/BrightnessLevelPreference.kt
+++ b/src/com/android/settings/display/BrightnessLevelPreference.kt
@@ -31,16 +31,20 @@
import com.android.settings.Utils
import com.android.settings.core.SettingsBaseActivity
import com.android.settingslib.RestrictedPreference
+import com.android.settingslib.datastore.AbstractKeyedDataObservable
+import com.android.settingslib.datastore.DataChangeReason
import com.android.settingslib.datastore.HandlerExecutor
+import com.android.settingslib.datastore.KeyValueStore
import com.android.settingslib.datastore.KeyedObserver
import com.android.settingslib.datastore.SettingsSystemStore
import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX
import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN
import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat
-import com.android.settingslib.metadata.PreferenceLifecycleContext
-import com.android.settingslib.metadata.PreferenceLifecycleProvider
+import com.android.settingslib.metadata.FloatPersistentPreference
import com.android.settingslib.metadata.PreferenceMetadata
import com.android.settingslib.metadata.PreferenceSummaryProvider
+import com.android.settingslib.metadata.ReadWritePermit
+import com.android.settingslib.metadata.SensitivityLevel
import com.android.settingslib.preference.PreferenceBinding
import com.android.settingslib.transition.SettingsTransitionHelper
import java.text.NumberFormat
@@ -48,15 +52,12 @@
// LINT.IfChange
class BrightnessLevelPreference :
PreferenceMetadata,
+ FloatPersistentPreference,
PreferenceBinding,
PreferenceRestrictionMixin,
PreferenceSummaryProvider,
- PreferenceLifecycleProvider,
Preference.OnPreferenceClickListener {
- private var brightnessObserver: KeyedObserver<String>? = null
- private var displayListener: DisplayListener? = null
-
override val key: String
get() = KEY
@@ -67,7 +68,7 @@
get() = R.string.keywords_display_brightness_level
override fun getSummary(context: Context): CharSequence? =
- NumberFormat.getPercentInstance().format(getCurrentBrightness(context))
+ NumberFormat.getPercentInstance().format(context.brightness)
override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context)
@@ -77,91 +78,113 @@
override val useAdminDisabledSummary: Boolean
get() = true
+ override fun intent(context: Context): Intent? =
+ Intent(ACTION_SHOW_BRIGHTNESS_DIALOG)
+ .setPackage(Utils.SYSTEMUI_PACKAGE_NAME)
+ .putExtra(
+ SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
+ SettingsTransitionHelper.TransitionType.TRANSITION_NONE,
+ )
+ .putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true)
+
override fun createWidget(context: Context) = RestrictedPreference(context)
override fun bind(preference: Preference, metadata: PreferenceMetadata) {
super.bind(preference, metadata)
preference.onPreferenceClickListener = this
+ preference.isPersistent = false
}
- override fun onStart(context: PreferenceLifecycleContext) {
- val observer = KeyedObserver<String> { _, _ -> context.notifyPreferenceChange(KEY) }
- brightnessObserver = observer
- SettingsSystemStore.get(context)
- .addObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, observer, HandlerExecutor.main)
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
+ ReadWritePermit.ALLOW
- val listener =
- object : DisplayListener {
- override fun onDisplayAdded(displayId: Int) {}
+ override fun getWritePermit(context: Context, value: Float?, callingPid: Int, callingUid: Int) =
+ ReadWritePermit.DISALLOW
- override fun onDisplayRemoved(displayId: Int) {}
+ override val sensitivityLevel
+ get() = SensitivityLevel.NO_SENSITIVITY
- override fun onDisplayChanged(displayId: Int) {
- context.notifyPreferenceChange(KEY)
- }
- }
- displayListener = listener
- context.displayManager.registerDisplayListener(
- listener,
- HandlerExecutor.main,
- /* eventFlags= */ 0,
- DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS,
- )
- }
+ override fun storage(context: Context): KeyValueStore = BrightnessStorage(context)
- override fun onStop(context: PreferenceLifecycleContext) {
- brightnessObserver?.let {
- SettingsSystemStore.get(context).removeObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, it)
- brightnessObserver = null
+ private class BrightnessStorage(private val context: Context) :
+ AbstractKeyedDataObservable<String>(),
+ KeyValueStore,
+ KeyedObserver<String>,
+ DisplayListener {
+
+ override fun contains(key: String) = key == KEY
+
+ @Suppress("UNCHECKED_CAST")
+ override fun <T : Any> getValue(key: String, valueType: Class<T>) =
+ context.brightness.toFloat() as T
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {}
+
+ override fun onFirstObserverAdded() {
+ SettingsSystemStore.get(context)
+ .addObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, this, HandlerExecutor.main)
+
+ context.displayManager.registerDisplayListener(
+ this,
+ HandlerExecutor.main,
+ /* eventFlags= */ 0,
+ DisplayManager.PRIVATE_EVENT_FLAG_DISPLAY_BRIGHTNESS,
+ )
}
- displayListener?.let {
- context.displayManager.unregisterDisplayListener(it)
- displayListener = null
+ override fun onLastObserverRemoved() {
+ SettingsSystemStore.get(context).removeObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, this)
+
+ context.displayManager.unregisterDisplayListener(this)
+ }
+
+ override fun onKeyChanged(key: String, reason: Int) {
+ notifyChange(KEY, DataChangeReason.UPDATE)
+ }
+
+ override fun onDisplayAdded(displayId: Int) {}
+
+ override fun onDisplayRemoved(displayId: Int) {}
+
+ override fun onDisplayChanged(displayId: Int) {
+ notifyChange(KEY, DataChangeReason.UPDATE)
}
}
- private val Context.displayManager: DisplayManager
- get() = getSystemService(DisplayManager::class.java)!!
-
override fun onPreferenceClick(preference: Preference): Boolean {
val context = preference.context
- val intent =
- Intent(ACTION_SHOW_BRIGHTNESS_DIALOG)
- .setPackage(Utils.SYSTEMUI_PACKAGE_NAME)
- .putExtra(
- SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE,
- SettingsTransitionHelper.TransitionType.TRANSITION_NONE,
- )
- .putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true)
val options =
ActivityOptions.makeCustomAnimation(
context,
android.R.anim.fade_in,
android.R.anim.fade_out,
)
- context.startActivityForResult(preference.key, intent, 0, options.toBundle())
+ context.startActivityForResult(preference.key, intent(context), 0, options.toBundle())
return true
}
- private fun getCurrentBrightness(context: Context): Double {
- val info: BrightnessInfo? = context.display.brightnessInfo
- val value =
- info?.run {
- convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum)
- }
- return getPercentage(value?.toDouble() ?: 0.0)
- }
-
- private fun getPercentage(value: Double): Double =
- when {
- value > GAMMA_SPACE_MAX -> 1.0
- value < GAMMA_SPACE_MIN -> 0.0
- else -> (value - GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN)
- }
-
companion object {
const val KEY = "brightness"
+
+ private val Context.displayManager: DisplayManager
+ get() = getSystemService(DisplayManager::class.java)!!
+
+ private val Context.brightness: Double
+ get() {
+ val info: BrightnessInfo? = display.brightnessInfo
+ val value =
+ info?.run {
+ convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum)
+ }
+ return getPercentage(value?.toDouble() ?: 0.0)
+ }
+
+ private fun getPercentage(value: Double): Double =
+ when {
+ value > GAMMA_SPACE_MAX -> 1.0
+ value < GAMMA_SPACE_MIN -> 0.0
+ else -> (value - GAMMA_SPACE_MIN) / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN)
+ }
}
}
// LINT.ThenChange(BrightnessLevelPreferenceController.java)
diff --git a/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizard.java b/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizard.java
index 6e002ae..197e4fe 100644
--- a/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizard.java
+++ b/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizard.java
@@ -20,8 +20,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
import com.android.settings.accessibility.Flags;
+import com.android.settingslib.RestrictedPreferenceHelper;
+import com.android.settingslib.RestrictedPreferenceHelperProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
/**
@@ -31,15 +35,35 @@
public class BrightnessLevelPreferenceControllerForSetupWizard extends
BrightnessLevelPreferenceController {
+ private RestrictedPreferenceHelper mRestrictedPreferenceHelper;
+
public BrightnessLevelPreferenceControllerForSetupWizard(@NonNull Context context,
@Nullable Lifecycle lifecycle) {
super(context, lifecycle);
}
+ private boolean isRestricted() {
+ if (mRestrictedPreferenceHelper == null) {
+ return false;
+ }
+ return mRestrictedPreferenceHelper.isDisabledByAdmin()
+ || mRestrictedPreferenceHelper.isDisabledByEcm();
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ Preference preference = screen.findPreference(getPreferenceKey());
+ if (preference instanceof RestrictedPreferenceHelperProvider helperProvider) {
+ mRestrictedPreferenceHelper = helperProvider.getRestrictedPreferenceHelper();
+ preference.setVisible(!isRestricted());
+ }
+ }
+
@Override
@AvailabilityStatus
public int getAvailabilityStatus() {
- if (!Flags.addBrightnessSettingsInSuw()) {
+ if (!Flags.addBrightnessSettingsInSuw() || isRestricted()) {
return CONDITIONALLY_UNAVAILABLE;
}
return super.getAvailabilityStatus();
diff --git a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
index 3240616..32d2910 100644
--- a/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
+++ b/src/com/android/settings/display/PeakRefreshRateSwitchPreference.kt
@@ -50,11 +50,15 @@
override fun storage(context: Context): KeyValueStore =
PeakRefreshRateStore(context, SettingsSystemStore.get(context))
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/display/darkmode/DarkModeScreen.kt b/src/com/android/settings/display/darkmode/DarkModeScreen.kt
index 7f8087a..807b0ae 100644
--- a/src/com/android/settings/display/darkmode/DarkModeScreen.kt
+++ b/src/com/android/settings/display/darkmode/DarkModeScreen.kt
@@ -71,11 +71,15 @@
override val keywords: Int
get() = R.string.keywords_dark_ui_mode
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt b/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt
index 95d73dd..49f219f 100644
--- a/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt
+++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreference.kt
@@ -102,10 +102,10 @@
override fun getMaxValue(context: Context): Int = 100
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Int?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) =
ReadWritePermit.DISALLOW
companion object {
diff --git a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverPreference.kt b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverPreference.kt
index 93da40d..ce1c1be 100644
--- a/src/com/android/settings/fuelgauge/batterysaver/BatterySaverPreference.kt
+++ b/src/com/android/settings/fuelgauge/batterysaver/BatterySaverPreference.kt
@@ -43,11 +43,15 @@
override fun storage(context: Context) = BatterySaverStore(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 5e17f4b..2edbf99 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -274,7 +274,6 @@
}
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
- animateBatteryChartViewGroup();
}
if (mBatteryChartViewGroup != null) {
final View grandparentView = (View) mBatteryChartViewGroup.getParent();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
index df84aba..b917d1f 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageDataLoader.java
@@ -118,6 +118,7 @@
final BatteryLevelData batteryLevelData =
DataProcessManager.getBatteryLevelData(
context,
+ null,
userIdsSeries,
/* isFromPeriodJob= */ true,
batteryDiffDataMap -> {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
index fd548ab..9992313 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessManager.java
@@ -18,12 +18,12 @@
import android.app.usage.UsageEvents;
import android.content.Context;
-import android.os.AsyncTask;
import android.util.ArrayMap;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.lifecycle.Lifecycle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -80,6 +80,7 @@
private final long mLastFullChargeTimestamp;
private final boolean mIsFromPeriodJob;
private final Context mContext;
+ private final @Nullable Lifecycle mLifecycle;
private final UserIdsSeries mUserIdsSeries;
private final OnBatteryDiffDataMapLoadedListener mCallbackFunction;
private final List<AppUsageEvent> mAppUsageEventList = new ArrayList<>();
@@ -120,6 +121,7 @@
/** Constructor when there exists battery level data. */
DataProcessManager(
Context context,
+ @Nullable Lifecycle lifecycle,
final UserIdsSeries userIdsSeries,
final boolean isFromPeriodJob,
final long rawStartTimestamp,
@@ -128,6 +130,7 @@
@NonNull final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay,
@NonNull final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
mContext = context.getApplicationContext();
+ mLifecycle = lifecycle;
mUserIdsSeries = userIdsSeries;
mIsFromPeriodJob = isFromPeriodJob;
mRawStartTimestamp = rawStartTimestamp;
@@ -140,9 +143,11 @@
/** Constructor when there is no battery level data. */
DataProcessManager(
Context context,
+ @Nullable Lifecycle lifecycle,
final UserIdsSeries userIdsSeries,
@NonNull final OnBatteryDiffDataMapLoadedListener callbackFunction) {
mContext = context.getApplicationContext();
+ mLifecycle = lifecycle;
mUserIdsSeries = userIdsSeries;
mCallbackFunction = callbackFunction;
mIsFromPeriodJob = false;
@@ -223,7 +228,7 @@
}
private void loadCurrentBatteryHistoryMap() {
- new AsyncTask<Void, Void, Map<String, BatteryHistEntry>>() {
+ new LifecycleAwareAsyncTask<Map<String, BatteryHistEntry>>(mLifecycle) {
@Override
protected Map<String, BatteryHistEntry> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
@@ -242,6 +247,7 @@
@Override
protected void onPostExecute(
final Map<String, BatteryHistEntry> currentBatteryHistoryMap) {
+ super.onPostExecute(currentBatteryHistoryMap);
if (mBatteryHistoryMap != null) {
// Replaces the placeholder in mBatteryHistoryMap.
for (Map.Entry<Long, Map<String, BatteryHistEntry>> mapEntry :
@@ -256,11 +262,11 @@
mIsCurrentBatteryHistoryLoaded = true;
tryToGenerateFinalDataAndApplyCallback();
}
- }.execute();
+ }.start();
}
private void loadCurrentAppUsageList() {
- new AsyncTask<Void, Void, List<AppUsageEvent>>() {
+ new LifecycleAwareAsyncTask<List<AppUsageEvent>>(mLifecycle) {
@Override
@Nullable
protected List<AppUsageEvent> doInBackground(Void... voids) {
@@ -299,6 +305,7 @@
@Override
protected void onPostExecute(final List<AppUsageEvent> currentAppUsageList) {
+ super.onPostExecute(currentAppUsageList);
if (currentAppUsageList == null || currentAppUsageList.isEmpty()) {
Log.d(TAG, "currentAppUsageList is null or empty");
} else {
@@ -307,11 +314,11 @@
mIsCurrentAppUsageLoaded = true;
tryToProcessAppUsageData();
}
- }.execute();
+ }.start();
}
private void loadDatabaseAppUsageList() {
- new AsyncTask<Void, Void, List<AppUsageEvent>>() {
+ new LifecycleAwareAsyncTask<List<AppUsageEvent>>(mLifecycle) {
@Override
protected List<AppUsageEvent> doInBackground(Void... voids) {
if (!shouldLoadAppUsageData()) {
@@ -337,6 +344,7 @@
@Override
protected void onPostExecute(final List<AppUsageEvent> databaseAppUsageList) {
+ super.onPostExecute(databaseAppUsageList);
if (databaseAppUsageList == null || databaseAppUsageList.isEmpty()) {
Log.d(TAG, "databaseAppUsageList is null or empty");
} else {
@@ -345,11 +353,11 @@
mIsDatabaseAppUsageLoaded = true;
tryToProcessAppUsageData();
}
- }.execute();
+ }.start();
}
private void loadPowerConnectionBatteryEventList() {
- new AsyncTask<Void, Void, List<BatteryEvent>>() {
+ new LifecycleAwareAsyncTask<List<BatteryEvent>>(mLifecycle) {
@Override
protected List<BatteryEvent> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
@@ -370,6 +378,7 @@
@Override
protected void onPostExecute(final List<BatteryEvent> batteryEventList) {
+ super.onPostExecute(batteryEventList);
if (batteryEventList == null || batteryEventList.isEmpty()) {
Log.d(TAG, "batteryEventList is null or empty");
} else {
@@ -379,11 +388,11 @@
mIsBatteryEventLoaded = true;
tryToProcessAppUsageData();
}
- }.execute();
+ }.start();
}
private void loadBatteryUsageSlotList() {
- new AsyncTask<Void, Void, List<BatteryUsageSlot>>() {
+ new LifecycleAwareAsyncTask<List<BatteryUsageSlot>>(mLifecycle) {
@Override
protected List<BatteryUsageSlot> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
@@ -402,6 +411,7 @@
@Override
protected void onPostExecute(final List<BatteryUsageSlot> batteryUsageSlotList) {
+ super.onPostExecute(batteryUsageSlotList);
if (batteryUsageSlotList == null || batteryUsageSlotList.isEmpty()) {
Log.d(TAG, "batteryUsageSlotList is null or empty");
} else {
@@ -411,11 +421,11 @@
mIsBatteryUsageSlotLoaded = true;
tryToGenerateFinalDataAndApplyCallback();
}
- }.execute();
+ }.start();
}
private void loadAndApplyBatteryMapFromServiceOnly() {
- new AsyncTask<Void, Void, Map<Long, BatteryDiffData>>() {
+ new LifecycleAwareAsyncTask<Map<Long, BatteryDiffData>>(mLifecycle) {
@Override
protected Map<Long, BatteryDiffData> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
@@ -437,11 +447,12 @@
@Override
protected void onPostExecute(final Map<Long, BatteryDiffData> batteryDiffDataMap) {
+ super.onPostExecute(batteryDiffDataMap);
if (mCallbackFunction != null) {
mCallbackFunction.onBatteryDiffDataMapLoaded(batteryDiffDataMap);
}
}
- }.execute();
+ }.start();
}
private void tryToProcessAppUsageData() {
@@ -481,7 +492,7 @@
}
private synchronized void generateFinalDataAndApplyCallback() {
- new AsyncTask<Void, Void, Map<Long, BatteryDiffData>>() {
+ new LifecycleAwareAsyncTask<Map<Long, BatteryDiffData>>(mLifecycle) {
@Override
protected Map<Long, BatteryDiffData> doInBackground(Void... voids) {
final long startTime = System.currentTimeMillis();
@@ -523,11 +534,12 @@
@Override
protected void onPostExecute(final Map<Long, BatteryDiffData> batteryDiffDataMap) {
+ super.onPostExecute(batteryDiffDataMap);
if (mCallbackFunction != null) {
mCallbackFunction.onBatteryDiffDataMapLoaded(batteryDiffDataMap);
}
}
- }.execute();
+ }.start();
}
// Whether we should load app usage data from service or database.
@@ -566,6 +578,7 @@
@Nullable
public static BatteryLevelData getBatteryLevelData(
Context context,
+ @Nullable Lifecycle lifecycle,
final UserIdsSeries userIdsSeries,
final boolean isFromPeriodJob,
final OnBatteryDiffDataMapLoadedListener onBatteryUsageMapLoadedListener) {
@@ -585,6 +598,7 @@
final BatteryLevelData batteryLevelData =
getPeriodBatteryLevelData(
context,
+ lifecycle,
userIdsSeries,
startTimestamp,
lastFullChargeTime,
@@ -604,6 +618,7 @@
private static BatteryLevelData getPeriodBatteryLevelData(
Context context,
+ @Nullable Lifecycle lifecycle,
final UserIdsSeries userIdsSeries,
final long startTimestamp,
final long lastFullChargeTime,
@@ -631,7 +646,8 @@
lastFullChargeTime);
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
Log.d(TAG, "batteryHistoryMap is null in getPeriodBatteryLevelData()");
- new DataProcessManager(context, userIdsSeries, onBatteryDiffDataMapLoadedListener)
+ new DataProcessManager(context, lifecycle, userIdsSeries,
+ onBatteryDiffDataMapLoadedListener)
.start();
return null;
}
@@ -660,7 +676,8 @@
DataProcessor.getLevelDataThroughProcessedHistoryMap(
context, processedBatteryHistoryMap);
if (batteryLevelData == null) {
- new DataProcessManager(context, userIdsSeries, onBatteryDiffDataMapLoadedListener)
+ new DataProcessManager(context, lifecycle, userIdsSeries,
+ onBatteryDiffDataMapLoadedListener)
.start();
Log.d(TAG, "getBatteryLevelData() returns null");
return null;
@@ -669,6 +686,7 @@
// Start the async task to compute diff usage data and load labels and icons.
new DataProcessManager(
context,
+ lifecycle,
userIdsSeries,
isFromPeriodJob,
startTimestamp,
diff --git a/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTask.kt b/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTask.kt
new file mode 100644
index 0000000..a715cb7
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTask.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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 android.os.AsyncTask
+import androidx.annotation.CallSuper
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import com.android.settingslib.datastore.HandlerExecutor.Companion.main as mainExecutor
+
+/**
+ * Lifecycle aware [AsyncTask] to cancel task automatically when [lifecycle] is stopped.
+ *
+ * Must call [start] instead of [execute] to run the task.
+ */
+abstract class LifecycleAwareAsyncTask<Result>(private val lifecycle: Lifecycle?) :
+ AsyncTask<Void, Void, Result>(), DefaultLifecycleObserver {
+
+ @CallSuper
+ override fun onPostExecute(result: Result) {
+ lifecycle?.removeObserver(this)
+ }
+
+ override fun onStop(owner: LifecycleOwner) {
+ cancel(false)
+ lifecycle?.removeObserver(this)
+ }
+
+ /**
+ * Starts the task, which invokes [execute] (cannot override [execute] as it is final).
+ *
+ * This method is expected to be invoked from main thread but current usage might call from
+ * background thread.
+ */
+ fun start() {
+ execute() // expects main thread
+ val lifecycle = lifecycle ?: return
+ mainExecutor.execute {
+ // Status is updated to FINISHED if onPoseExecute happened before. And task is cancelled
+ // if lifecycle is stopped.
+ if (status == Status.RUNNING && !isCancelled) {
+ lifecycle.addObserver(this) // requires main thread
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index 1ed6a74..b8188b8 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -32,6 +32,8 @@
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
@@ -150,6 +152,13 @@
}
@Override
+ protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
+ final RecyclerView.Adapter adapter = super.onCreateAdapter(preferenceScreen);
+ adapter.setHasStableIds(true);
+ return adapter;
+ }
+
+ @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
mBatteryTipsController = new BatteryTipsController(context);
@@ -503,9 +512,11 @@
@Override
public BatteryLevelData loadInBackground() {
+ Context context = getContext();
return DataProcessManager.getBatteryLevelData(
- getContext(),
- new UserIdsSeries(getContext(), /* isNonUIRequest= */ false),
+ context,
+ getLifecycle(),
+ new UserIdsSeries(context, /* isNonUIRequest= */ false),
/* isFromPeriodJob= */ false,
PowerUsageAdvanced.this::onBatteryDiffDataMapUpdate);
}
diff --git a/src/com/android/settings/gestures/OneHandedSettings.java b/src/com/android/settings/gestures/OneHandedSettings.java
index 0378888..0a2599e 100644
--- a/src/com/android/settings/gestures/OneHandedSettings.java
+++ b/src/com/android/settings/gestures/OneHandedSettings.java
@@ -22,9 +22,9 @@
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+import android.widget.CompoundButton;
import androidx.recyclerview.widget.RecyclerView;
@@ -33,7 +33,6 @@
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityFragmentUtils;
import com.android.settings.accessibility.AccessibilityShortcutPreferenceFragment;
-import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.search.SearchIndexableRaw;
@@ -82,12 +81,7 @@
final MainSwitchPreference mainSwitchPreference =
getPreferenceScreen().findPreference(ONE_HANDED_MAIN_SWITCH_KEY);
- mainSwitchPreference.addOnSwitchChangeListener((switchView, isChecked) -> {
- switchView.setChecked(isChecked);
- if (isChecked) {
- showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE);
- }
- });
+ mainSwitchPreference.addOnSwitchChangeListener(CompoundButton::setChecked);
}
@Override
@@ -146,24 +140,6 @@
}
@Override
- protected ComponentName getTileComponentName() {
- return AccessibilityShortcutController.ONE_HANDED_TILE_COMPONENT_NAME;
- }
-
- @Override
- protected CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
- final Context context = getContext();
- if (context == null) {
- Log.w(TAG, "OneHandedSettings not attached to a context.");
- return null;
- }
- return type == QuickSettingsTooltipType.GUIDE_TO_EDIT
- ? context.getText(R.string.accessibility_one_handed_mode_qs_tooltip_content)
- : context.getText(
- R.string.accessibility_one_handed_mode_auto_added_qs_tooltip_content);
- }
-
- @Override
protected int getPreferenceScreenResId() {
return R.xml.one_handed_settings;
}
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
deleted file mode 100644
index a4baa91..0000000
--- a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Copyright (C) 2012 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.inputmethod;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.hardware.input.InputDeviceIdentifier;
-import android.hardware.input.InputManager;
-import android.hardware.input.InputManager.InputDeviceListener;
-import android.hardware.input.KeyboardLayout;
-import android.os.Bundle;
-import android.view.InputDevice;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.CheckedTextView;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.loader.app.LoaderManager.LoaderCallbacks;
-import androidx.loader.content.AsyncTaskLoader;
-import androidx.loader.content.Loader;
-
-import com.android.settings.R;
-import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-public class KeyboardLayoutDialogFragment extends InstrumentedDialogFragment
- implements InputDeviceListener, LoaderCallbacks<KeyboardLayoutDialogFragment.Keyboards> {
- private static final String KEY_INPUT_DEVICE_IDENTIFIER = "inputDeviceIdentifier";
-
- private InputDeviceIdentifier mInputDeviceIdentifier;
- private int mInputDeviceId = -1;
- private InputManager mIm;
- private KeyboardLayoutAdapter mAdapter;
-
- public KeyboardLayoutDialogFragment() {
- }
-
- public KeyboardLayoutDialogFragment(InputDeviceIdentifier inputDeviceIdentifier) {
- mInputDeviceIdentifier = inputDeviceIdentifier;
- }
-
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DIALOG_KEYBOARD_LAYOUT;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- Context context = activity.getBaseContext();
- mIm = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
- mAdapter = new KeyboardLayoutAdapter(context);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (savedInstanceState != null) {
- mInputDeviceIdentifier = savedInstanceState.getParcelable(KEY_INPUT_DEVICE_IDENTIFIER);
- }
-
- getLoaderManager().initLoader(0, null, this);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putParcelable(KEY_INPUT_DEVICE_IDENTIFIER, mInputDeviceIdentifier);
- }
-
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- Context context = getActivity();
- LayoutInflater inflater = LayoutInflater.from(context);
- AlertDialog.Builder builder = new AlertDialog.Builder(context)
- .setTitle(R.string.keyboard_layout_dialog_title)
- .setPositiveButton(R.string.keyboard_layout_dialog_setup_button,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onSetupLayoutsButtonClicked();
- }
- })
- .setSingleChoiceItems(mAdapter, -1,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onKeyboardLayoutClicked(which);
- }
- })
- .setView(inflater.inflate(R.layout.keyboard_layout_dialog_switch_hint, null));
- updateSwitchHintVisibility();
- return builder.create();
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- mIm.registerInputDeviceListener(this, null);
-
- InputDevice inputDevice =
- mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
- if (inputDevice == null) {
- dismiss();
- return;
- }
- mInputDeviceId = inputDevice.getId();
- }
-
- @Override
- public void onPause() {
- mIm.unregisterInputDeviceListener(this);
- mInputDeviceId = -1;
-
- super.onPause();
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- super.onCancel(dialog);
- dismiss();
- }
-
- private void onSetupLayoutsButtonClicked() {
- ((OnSetupKeyboardLayoutsListener)getTargetFragment()).onSetupKeyboardLayouts(
- mInputDeviceIdentifier);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- show(getActivity().getSupportFragmentManager(), "layout");
- }
-
- private void onKeyboardLayoutClicked(int which) {
- if (which >= 0 && which < mAdapter.getCount()) {
- KeyboardLayout keyboardLayout = mAdapter.getItem(which);
- if (keyboardLayout != null) {
- mIm.setCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
- keyboardLayout.getDescriptor());
- }
- dismiss();
- }
- }
-
- @Override
- public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
- return new KeyboardLayoutLoader(getActivity().getBaseContext(), mInputDeviceIdentifier);
- }
-
- @Override
- public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
- mAdapter.clear();
- mAdapter.addAll(data.keyboardLayouts);
- mAdapter.setCheckedItem(data.current);
- AlertDialog dialog = (AlertDialog)getDialog();
- if (dialog != null) {
- dialog.getListView().setItemChecked(data.current, true);
- }
- updateSwitchHintVisibility();
- }
-
- @Override
- public void onLoaderReset(Loader<Keyboards> loader) {
- mAdapter.clear();
- updateSwitchHintVisibility();
- }
-
- @Override
- public void onInputDeviceAdded(int deviceId) {
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
- getLoaderManager().restartLoader(0, null, this);
- }
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
- dismiss();
- }
- }
-
- private void updateSwitchHintVisibility() {
- AlertDialog dialog = (AlertDialog)getDialog();
- if (dialog != null) {
- View customPanel = dialog.findViewById(com.google.android.material.R.id.customPanel);
- customPanel.setVisibility(mAdapter.getCount() > 1 ? View.VISIBLE : View.GONE);
- }
- }
-
- private static final class KeyboardLayoutAdapter extends ArrayAdapter<KeyboardLayout> {
- private final LayoutInflater mInflater;
- private int mCheckedItem = -1;
-
- public KeyboardLayoutAdapter(Context context) {
- super(context, com.android.internal.R.layout.simple_list_item_2_single_choice);
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- public void setCheckedItem(int position) {
- mCheckedItem = position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- KeyboardLayout item = getItem(position);
- String label, collection;
- if (item != null) {
- label = item.getLabel();
- collection = item.getCollection();
- } else {
- label = getContext().getString(R.string.keyboard_layout_default_label);
- collection = "";
- }
-
- boolean checked = (position == mCheckedItem);
- if (collection.isEmpty()) {
- return inflateOneLine(convertView, parent, label, checked);
- } else {
- return inflateTwoLine(convertView, parent, label, collection, checked);
- }
- }
-
- private View inflateOneLine(View convertView, ViewGroup parent,
- String label, boolean checked) {
- View view = convertView;
- if (view == null || isTwoLine(view)) {
- view = mInflater.inflate(
- com.android.internal.R.layout.simple_list_item_single_choice,
- parent, false);
- setTwoLine(view, false);
- }
- CheckedTextView headline = (CheckedTextView) view.findViewById(android.R.id.text1);
- headline.setText(label);
- headline.setChecked(checked);
- return view;
- }
-
- private View inflateTwoLine(View convertView, ViewGroup parent,
- String label, String collection, boolean checked) {
- View view = convertView;
- if (view == null || !isTwoLine(view)) {
- view = mInflater.inflate(
- com.android.internal.R.layout.simple_list_item_2_single_choice,
- parent, false);
- setTwoLine(view, true);
- }
- TextView headline = (TextView) view.findViewById(android.R.id.text1);
- TextView subText = (TextView) view.findViewById(android.R.id.text2);
- RadioButton radioButton =
- (RadioButton)view.findViewById(com.android.internal.R.id.radio);
- headline.setText(label);
- subText.setText(collection);
- radioButton.setChecked(checked);
- return view;
- }
-
- private static boolean isTwoLine(View view) {
- return view.getTag() == Boolean.TRUE;
- }
-
- private static void setTwoLine(View view, boolean twoLine) {
- view.setTag(Boolean.valueOf(twoLine));
- }
- }
-
- private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
- private final InputDeviceIdentifier mInputDeviceIdentifier;
-
- public KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier) {
- super(context);
- mInputDeviceIdentifier = inputDeviceIdentifier;
- }
-
- @Override
- public Keyboards loadInBackground() {
- Keyboards keyboards = new Keyboards();
- InputManager im = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
- if (mInputDeviceIdentifier == null || InputPeripheralsSettingsUtils.getInputDevice(
- im, mInputDeviceIdentifier) == null) {
- keyboards.keyboardLayouts.add(null); // default layout
- keyboards.current = 0;
- return keyboards;
- }
- String[] keyboardLayoutDescriptors = im.getEnabledKeyboardLayoutsForInputDevice(
- mInputDeviceIdentifier);
- for (String keyboardLayoutDescriptor : keyboardLayoutDescriptors) {
- KeyboardLayout keyboardLayout = im.getKeyboardLayout(keyboardLayoutDescriptor);
- if (keyboardLayout != null) {
- keyboards.keyboardLayouts.add(keyboardLayout);
- }
- }
- Collections.sort(keyboards.keyboardLayouts);
-
- String currentKeyboardLayoutDescriptor =
- im.getCurrentKeyboardLayoutForInputDevice(mInputDeviceIdentifier);
- if (currentKeyboardLayoutDescriptor != null) {
- final int numKeyboardLayouts = keyboards.keyboardLayouts.size();
- for (int i = 0; i < numKeyboardLayouts; i++) {
- if (keyboards.keyboardLayouts.get(i).getDescriptor().equals(
- currentKeyboardLayoutDescriptor)) {
- keyboards.current = i;
- break;
- }
- }
- }
-
- if (keyboards.keyboardLayouts.isEmpty()) {
- keyboards.keyboardLayouts.add(null); // default layout
- keyboards.current = 0;
- }
- return keyboards;
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- forceLoad();
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
- }
-
- public static final class Keyboards {
- public final ArrayList<KeyboardLayout> keyboardLayouts = new ArrayList<KeyboardLayout>();
- public int current = -1;
- }
-
- public interface OnSetupKeyboardLayoutsListener {
- public void onSetupKeyboardLayouts(InputDeviceIdentifier mInputDeviceIdentifier);
- }
-}
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java
deleted file mode 100644
index 97d8ffc..0000000
--- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerController.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.inputmethod;
-
-
-import android.content.Context;
-import android.hardware.input.InputDeviceIdentifier;
-import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardLayout;
-
-import androidx.fragment.app.Fragment;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-import androidx.preference.SwitchPreferenceCompat;
-import androidx.preference.TwoStatePreference;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnStart;
-import com.android.settingslib.core.lifecycle.events.OnStop;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-public class KeyboardLayoutPickerController extends BasePreferenceController implements
- InputManager.InputDeviceListener, LifecycleObserver, OnStart, OnStop {
-
- private final InputManager mIm;
- private final Map<TwoStatePreference, KeyboardLayout> mPreferenceMap;
-
- private Fragment mParent;
- private int mInputDeviceId;
- private InputDeviceIdentifier mInputDeviceIdentifier;
- private KeyboardLayout[] mKeyboardLayouts;
- private PreferenceScreen mScreen;
-
-
- public KeyboardLayoutPickerController(Context context, String key) {
- super(context, key);
- mIm = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
- mInputDeviceId = -1;
- mPreferenceMap = new HashMap<>();
- }
-
- public void initialize(Fragment parent, InputDeviceIdentifier inputDeviceIdentifier) {
- mParent = parent;
- mInputDeviceIdentifier = inputDeviceIdentifier;
- mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier);
- Arrays.sort(mKeyboardLayouts);
- }
-
- @Override
- public void onStart() {
- mIm.registerInputDeviceListener(this, null);
- if (mInputDeviceIdentifier == null
- || InputPeripheralsSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier)
- == null) {
- return;
- }
- mInputDeviceId =
- InputPeripheralsSettingsUtils.getInputDevice(mIm,
- mInputDeviceIdentifier).getId();
- updateCheckedState();
- }
-
- @Override
- public void onStop() {
- mIm.unregisterInputDeviceListener(this);
- mInputDeviceId = -1;
- }
-
- @Override
- public void displayPreference(PreferenceScreen screen) {
- super.displayPreference(screen);
- mScreen = screen;
- createPreferenceHierarchy();
- }
-
- @Override
- public int getAvailabilityStatus() {
- return AVAILABLE;
- }
-
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (!(preference instanceof TwoStatePreference switchPref)) {
- return false;
- }
-
- final KeyboardLayout layout = mPreferenceMap.get(switchPref);
- if (layout != null) {
- final boolean checked = switchPref.isChecked();
- if (checked) {
- mIm.addKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
- layout.getDescriptor());
- } else {
- mIm.removeKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
- layout.getDescriptor());
- }
- }
- return true;
- }
-
- @Override
- public void onInputDeviceAdded(int deviceId) {
-
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
- mParent.getActivity().finish();
- }
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
- updateCheckedState();
- }
- }
-
- private void updateCheckedState() {
- final String[] enabledKeyboardLayouts = mIm.getEnabledKeyboardLayoutsForInputDevice(
- mInputDeviceIdentifier);
- Arrays.sort(enabledKeyboardLayouts);
-
- for (Map.Entry<TwoStatePreference, KeyboardLayout> entry : mPreferenceMap.entrySet()) {
- entry.getKey().setChecked(Arrays.binarySearch(enabledKeyboardLayouts,
- entry.getValue().getDescriptor()) >= 0);
- }
- }
-
- private void createPreferenceHierarchy() {
- if (mKeyboardLayouts == null) {
- return;
- }
- for (KeyboardLayout layout : mKeyboardLayouts) {
- final TwoStatePreference pref = new SwitchPreferenceCompat(mScreen.getContext());
- pref.setTitle(layout.getLabel());
- pref.setSummary(layout.getCollection());
- // TODO: Waiting for new API to use a prefix with special number to setKey
- pref.setKey(layout.getDescriptor());
- mScreen.addPreference(pref);
- mPreferenceMap.put(pref, layout);
- }
- }
-}
-
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
deleted file mode 100644
index c21ac63..0000000
--- a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.inputmethod;
-
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.hardware.input.InputDeviceIdentifier;
-import android.hardware.input.InputManager;
-
-import com.android.settings.R;
-import com.android.settings.dashboard.DashboardFragment;
-
-public class KeyboardLayoutPickerFragment extends DashboardFragment {
-
- private static final String TAG = "KeyboardLayoutPicker";
-
- /**
- * Intent extra: The input device descriptor of the keyboard whose keyboard
- * layout is to be changed.
- */
- public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.INPUTMETHOD_KEYBOARD;
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
-
- final InputDeviceIdentifier inputDeviceIdentifier = getActivity().getIntent().
- getParcelableExtra(EXTRA_INPUT_DEVICE_IDENTIFIER);
- final InputManager im = context.getSystemService(InputManager.class);
- if (InputPeripheralsSettingsUtils.getInputDevice(im, inputDeviceIdentifier) == null) {
- return;
- }
- use(KeyboardLayoutPickerController.class).initialize(this /*parent*/,
- inputDeviceIdentifier);
- }
-
- @Override
- protected String getLogTag() {
- return TAG;
- }
-
- protected int getPreferenceScreenResId() {
- return R.xml.keyboard_layout_picker_fragment;
- }
-}
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index fcb672d..8aa5ac7 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -20,12 +20,10 @@
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.database.ContentObserver;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputSettings;
-import android.hardware.input.KeyboardLayout;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -47,7 +45,6 @@
import com.android.internal.util.Preconditions;
import com.android.settings.R;
-import com.android.settings.Settings;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.keyboard.Flags;
@@ -66,8 +63,7 @@
// controllers between here and A11y Setting page.
@SearchIndexable
public final class PhysicalKeyboardFragment extends DashboardFragment
- implements InputManager.InputDeviceListener,
- KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener {
+ implements InputManager.InputDeviceListener {
private static final String KEYBOARD_OPTIONS_CATEGORY = "keyboard_options_category";
private static final String KEYBOARD_A11Y_CATEGORY = "keyboard_a11y_category";
@@ -79,6 +75,7 @@
private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper";
private static final String MODIFIER_KEYS_SETTINGS = "modifier_keys_settings";
private static final String EXTRA_AUTO_SELECTION = "auto_selection";
+ public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
private static final String TAG = "KeyboardAndTouchA11yFragment";
private static final Uri sVirtualKeyboardSettingsUri = Secure.getUriFor(
Secure.SHOW_IME_WITH_HARD_KEYBOARD);
@@ -112,13 +109,8 @@
private TwoStatePreference mAccessibilityStickyKeys = null;
@Nullable
private TwoStatePreference mAccessibilityMouseKeys = null;
-
- private Intent mIntentWaitingForResult;
private boolean mSupportsFirmwareUpdate;
- static final String EXTRA_BT_ADDRESS = "extra_bt_address";
- private String mBluetoothAddress;
-
@Override
protected String getLogTag() {
return TAG;
@@ -197,8 +189,7 @@
mKeyboardA11yCategory.setVisible(false);
}
InputDeviceIdentifier inputDeviceIdentifier = activity.getIntent().getParcelableExtra(
- KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
- InputDeviceIdentifier.class);
+ EXTRA_INPUT_DEVICE_IDENTIFIER, InputDeviceIdentifier.class);
int intentFromWhere =
activity.getIntent().getIntExtra(android.provider.Settings.EXTRA_ENTRYPOINT, -1);
if (intentFromWhere != -1) {
@@ -360,13 +351,6 @@
}
}
- private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
- KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
- inputDeviceIdentifier);
- fragment.setTargetFragment(this, 0);
- fragment.show(getActivity().getSupportFragmentManager(), "keyboardLayout");
- }
-
private void showEnabledLocalesKeyboardLayoutList(InputDeviceIdentifier inputDeviceIdentifier) {
Bundle arguments = new Bundle();
arguments.putParcelable(InputPeripheralsSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER,
@@ -505,45 +489,6 @@
}
};
- @Override
- public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
- final Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
- intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
- inputDeviceIdentifier);
- mIntentWaitingForResult = intent;
- startActivityForResult(intent, 0);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (mIntentWaitingForResult != null) {
- InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
- .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
- InputDeviceIdentifier.class);
- mIntentWaitingForResult = null;
- showKeyboardLayoutDialog(inputDeviceIdentifier);
- }
- }
-
- private static String getLayoutLabel(@NonNull InputDevice device,
- @NonNull Context context, @NonNull InputManager im) {
- final String currentLayoutDesc =
- im.getCurrentKeyboardLayoutForInputDevice(device.getIdentifier());
- if (currentLayoutDesc == null) {
- return context.getString(R.string.keyboard_layout_default_label);
- }
- final KeyboardLayout currentLayout = im.getKeyboardLayout(currentLayoutDesc);
- if (currentLayout == null) {
- return context.getString(R.string.keyboard_layout_default_label);
- }
- // If current layout is specified but the layout is null, just return an empty string
- // instead of falling back to R.string.keyboard_layout_default_label.
- return TextUtils.emptyIfNull(currentLayout.getLabel());
- }
-
@NonNull
static List<HardKeyboardDeviceInfo> getHardKeyboards(@NonNull Context context) {
final List<HardKeyboardDeviceInfo> keyboards = new ArrayList<>();
@@ -559,7 +504,6 @@
keyboards.add(new HardKeyboardDeviceInfo(
device.getName(),
device.getIdentifier(),
- getLayoutLabel(device, context, im),
device.getBluetoothAddress(),
device.getVendorId(),
device.getProductId()));
@@ -572,12 +516,8 @@
if (result != 0) {
return result;
}
- result = a.mDeviceIdentifier.getDescriptor().compareTo(
+ return a.mDeviceIdentifier.getDescriptor().compareTo(
b.mDeviceIdentifier.getDescriptor());
- if (result != 0) {
- return result;
- }
- return collator.compare(a.mLayoutLabel, b.mLayoutLabel);
});
return keyboards;
}
@@ -587,8 +527,6 @@
public final String mDeviceName;
@NonNull
public final InputDeviceIdentifier mDeviceIdentifier;
- @NonNull
- public final String mLayoutLabel;
@Nullable
public final String mBluetoothAddress;
@NonNull
@@ -599,13 +537,11 @@
public HardKeyboardDeviceInfo(
@Nullable String deviceName,
@NonNull InputDeviceIdentifier deviceIdentifier,
- @NonNull String layoutLabel,
@Nullable String bluetoothAddress,
@NonNull int vendorId,
@NonNull int productId) {
mDeviceName = TextUtils.emptyIfNull(deviceName);
mDeviceIdentifier = deviceIdentifier;
- mLayoutLabel = layoutLabel;
mBluetoothAddress = bluetoothAddress;
mVendorId = vendorId;
mProductId = productId;
@@ -625,9 +561,6 @@
if (!Objects.equals(mDeviceIdentifier, that.mDeviceIdentifier)) {
return false;
}
- if (!TextUtils.equals(mLayoutLabel, that.mLayoutLabel)) {
- return false;
- }
if (!TextUtils.equals(mBluetoothAddress, that.mBluetoothAddress)) {
return false;
}
diff --git a/src/com/android/settings/inputmethod/PointerFillStylePreference.java b/src/com/android/settings/inputmethod/PointerFillStylePreference.java
index 74284d6..9c9d076 100644
--- a/src/com/android/settings/inputmethod/PointerFillStylePreference.java
+++ b/src/com/android/settings/inputmethod/PointerFillStylePreference.java
@@ -25,6 +25,9 @@
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.StateListDrawable;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.PointerIcon;
@@ -41,6 +44,11 @@
public class PointerFillStylePreference extends Preference {
+ private static final int[] STATE_HOVERED_SELECTED =
+ new int[]{android.R.attr.state_hovered, android.R.attr.state_selected};
+ private static final int[] STATE_SELECTED = new int[]{android.R.attr.state_selected};
+ private static final int[] STATE_HOVERED = new int[]{android.R.attr.state_hovered};
+ private static final int[] STATE_DEFAULT = new int[]{};
@Nullable private LinearLayout mButtonHolder;
@@ -82,11 +90,7 @@
if (button == null) {
return;
}
- int[] attrs = {com.android.internal.R.attr.pointerIconVectorFill};
- try (TypedArray ta = getContext().obtainStyledAttributes(
- PointerIcon.vectorFillStyleToResource(style), attrs)) {
- button.getBackground().setTint(ta.getColor(0, Color.BLACK));
- }
+ tintButtonByStyle(button, style);
button.setOnClickListener(
(v) -> {
getPreferenceDataStore().putInt(Settings.System.POINTER_FILL_STYLE, style);
@@ -96,6 +100,32 @@
button.setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_ARROW));
}
+ private void tintButtonByStyle(ImageView button, int style) {
+ int[] attrs = {com.android.internal.R.attr.pointerIconVectorFill};
+ try (TypedArray ta = getContext().obtainStyledAttributes(
+ PointerIcon.vectorFillStyleToResource(style), attrs)) {
+ // Index 0, as there is only one attribute returned here.
+ int color = ta.getColor(/* index= */ 0, Color.BLACK);
+ StateListDrawable stateListDrawable = (StateListDrawable) button.getDrawable();
+ tintDrawableByLayerId(stateListDrawable, STATE_HOVERED_SELECTED,
+ R.id.tintableCircleHoveredSelected, color);
+ tintDrawableByLayerId(stateListDrawable, STATE_SELECTED, R.id.tintableCircleSelected,
+ color);
+ tintDrawableByLayerId(stateListDrawable, STATE_HOVERED, R.id.tintableCircleHovered,
+ color);
+ tintDrawableByLayerId(stateListDrawable, STATE_DEFAULT, R.id.tintableCircleDefault,
+ color);
+ }
+ }
+
+ private void tintDrawableByLayerId(StateListDrawable stateListDrawable, int[] stateSet,
+ int layerId, int color) {
+ int index = stateListDrawable.findStateDrawableIndex(stateSet);
+ LayerDrawable layerDrawable = (LayerDrawable) stateListDrawable.getStateDrawable(index);
+ Drawable drawable = layerDrawable.findDrawableByLayerId(layerId);
+ drawable.setTint(color);
+ }
+
private void setButtonChecked(int id) {
if (mButtonHolder == null) {
return;
diff --git a/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java b/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
index 86e044c..163027b 100644
--- a/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
+++ b/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
@@ -26,6 +26,7 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.keyboard.Flags;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -85,13 +86,15 @@
@Override
public int getAvailabilityStatus() {
+ boolean isNewPageFlagDisabled = !Flags.keyboardAndTouchpadA11yNewPageEnabled();
boolean isFeatureOn = FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD);
boolean isTouchpad = InputPeripheralsSettingsUtils.isTouchpad();
boolean isPointerCustomizationEnabled =
android.view.flags.Flags.enableVectorCursorA11ySettings();
boolean isMouse = InputPeripheralsSettingsUtils.isMouse();
- return (isFeatureOn && isTouchpad) || (isPointerCustomizationEnabled && isMouse) ? AVAILABLE
+ return ((isFeatureOn && isTouchpad) || (isPointerCustomizationEnabled && isMouse))
+ && isNewPageFlagDisabled ? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
}
diff --git a/src/com/android/settings/language/DefaultVoiceInputPreferenceController.java b/src/com/android/settings/language/DefaultVoiceInputPreferenceController.java
index 74c156c..c0b65df 100644
--- a/src/com/android/settings/language/DefaultVoiceInputPreferenceController.java
+++ b/src/com/android/settings/language/DefaultVoiceInputPreferenceController.java
@@ -22,9 +22,12 @@
import android.content.pm.PackageManager;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
+import com.android.internal.annotations.Initializer;
import com.android.settings.applications.defaultapps.DefaultAppPreferenceController;
import com.android.settingslib.applications.DefaultAppInfo;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -43,7 +46,8 @@
private Preference mPreference;
private Context mContext;
- public DefaultVoiceInputPreferenceController(Context context, Lifecycle lifecycle) {
+ public DefaultVoiceInputPreferenceController(
+ @NonNull Context context, @Nullable Lifecycle lifecycle) {
super(context);
mContext = context;
mHelper = new VoiceInputHelper(context);
@@ -65,6 +69,7 @@
}
@Override
+ @Initializer
public void displayPreference(PreferenceScreen screen) {
mScreen = screen;
mPreference = screen.findPreference(getPreferenceKey());
diff --git a/src/com/android/settings/language/LanguageAndRegionPreferenceController.java b/src/com/android/settings/language/LanguageAndRegionPreferenceController.java
new file mode 100644
index 0000000..4e554ed
--- /dev/null
+++ b/src/com/android/settings/language/LanguageAndRegionPreferenceController.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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.language;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
+
+/**
+ * This is a display controller for new language activity entry.
+ * TODO(b/379962955): When new layout is on board, all old layouts should be removed.
+ */
+public class LanguageAndRegionPreferenceController extends BasePreferenceController {
+
+ public LanguageAndRegionPreferenceController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (!Flags.regionalPreferencesApiEnabled()) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ // TODO: Add setComponentEnabledSetting after LanguageAndRegionSettingsActivity is created.
+ return AVAILABLE;
+ }
+}
diff --git a/src/com/android/settings/language/LanguageAndRegionSettings.java b/src/com/android/settings/language/LanguageAndRegionSettings.java
new file mode 100644
index 0000000..a405878
--- /dev/null
+++ b/src/com/android/settings/language/LanguageAndRegionSettings.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 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.language;
+
+import android.app.Activity;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.flags.Flags;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SearchIndexable
+public class LanguageAndRegionSettings extends DashboardFragment {
+
+ private static final String KEY_SPEECH_CATEGORY = "speech_category";
+ private static final String KEY_ON_DEVICE_RECOGNITION = "on_device_recognition_settings";
+ private static final String KEY_TEXT_TO_SPEECH = "tts_settings_summary";
+
+ private static final String TAG = "LanguageAndRegionSettings";
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.SETTINGS_LANGUAGES_CATEGORY;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ // Hack to update action bar title. It's necessary to refresh title because this page user
+ // can change locale from here and fragment won't relaunch. Once language changes, title
+ // must display in the new language.
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.setTitle(R.string.languages_settings);
+ }
+
+ @Override
+ public @Nullable String getPreferenceScreenBindingKey(@NonNull Context context) {
+ return LanguageSettingScreen.KEY;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.language_and_region_settings;
+ }
+
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ return buildPreferenceControllers(context, getSettingsLifecycle());
+ }
+
+ private static List<AbstractPreferenceController> buildPreferenceControllers(
+ @NonNull Context context, @Nullable Lifecycle lifecycle) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+
+ final DefaultVoiceInputPreferenceController defaultVoiceInputPreferenceController =
+ new DefaultVoiceInputPreferenceController(context, lifecycle);
+ final TtsPreferenceController ttsPreferenceController =
+ new TtsPreferenceController(context, KEY_TEXT_TO_SPEECH);
+ final OnDeviceRecognitionPreferenceController onDeviceRecognitionPreferenceController =
+ new OnDeviceRecognitionPreferenceController(context, KEY_ON_DEVICE_RECOGNITION);
+
+ controllers.add(defaultVoiceInputPreferenceController);
+ controllers.add(ttsPreferenceController);
+ List<AbstractPreferenceController> speechCategoryChildren = new ArrayList<>(
+ List.of(defaultVoiceInputPreferenceController, ttsPreferenceController));
+
+ if (onDeviceRecognitionPreferenceController.isAvailable()) {
+ controllers.add(onDeviceRecognitionPreferenceController);
+ speechCategoryChildren.add(onDeviceRecognitionPreferenceController);
+ }
+
+ controllers.add(new PreferenceCategoryController(context, KEY_SPEECH_CATEGORY)
+ .setChildren(speechCategoryChildren));
+
+ return controllers;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.language_and_region_settings) {
+
+ @Override
+ @NonNull
+ public List<AbstractPreferenceController> createPreferenceControllers(
+ @NonNull Context context) {
+ return buildPreferenceControllers(context, null);
+ }
+
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ if (Flags.regionalPreferencesApiEnabled()) {
+ return true;
+ }
+ return false;
+ }
+ };
+}
diff --git a/src/com/android/settings/language/LanguagePreferenceController.java b/src/com/android/settings/language/LanguagePreferenceController.java
index 84624a2..90aaec4 100644
--- a/src/com/android/settings/language/LanguagePreferenceController.java
+++ b/src/com/android/settings/language/LanguagePreferenceController.java
@@ -22,6 +22,7 @@
import com.android.settings.Settings;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
/**
* This is a display controller for new language activity entry.
@@ -34,6 +35,10 @@
@Override
public int getAvailabilityStatus() {
+ if (Flags.regionalPreferencesApiEnabled()) {
+ setActivityEnabled(mContext, Settings.LanguageSettingsActivity.class, false);
+ return CONDITIONALLY_UNAVAILABLE;
+ }
setActivityEnabled(mContext, Settings.LanguageSettingsActivity.class, true);
return AVAILABLE;
}
diff --git a/src/com/android/settings/language/LanguageSettings.java b/src/com/android/settings/language/LanguageSettings.java
index d992ff2..58df053 100644
--- a/src/com/android/settings/language/LanguageSettings.java
+++ b/src/com/android/settings/language/LanguageSettings.java
@@ -25,6 +25,7 @@
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.flags.Flags;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.widget.PreferenceCategoryController;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -116,6 +117,9 @@
}
@Override
protected boolean isPageSearchEnabled(Context context) {
+ if (Flags.regionalPreferencesApiEnabled()) {
+ return false;
+ }
return true;
}
};
diff --git a/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt b/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt
index c29ec6e..77a141e 100644
--- a/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt
+++ b/src/com/android/settings/network/AdaptiveConnectivityTogglePreference.kt
@@ -35,11 +35,15 @@
override fun storage(context: Context): KeyValueStore =
AdaptiveConnectivityToggleStorage(context, SettingsSecureStore.get(context))
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.NO_SENSITIVITY
diff --git a/src/com/android/settings/network/AirplaneModePreference.kt b/src/com/android/settings/network/AirplaneModePreference.kt
index e3b7f55..c9377eb 100644
--- a/src/com/android/settings/network/AirplaneModePreference.kt
+++ b/src/com/android/settings/network/AirplaneModePreference.kt
@@ -65,10 +65,15 @@
override val restrictionKeys
get() = arrayOf(UserManager.DISALLOW_AIRPLANE_MODE)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) =
when {
isSatelliteOn(context) || isInEcmMode(context) -> ReadWritePermit.DISALLOW
else -> ReadWritePermit.ALLOW
diff --git a/src/com/android/settings/network/MobileDataPreference.kt b/src/com/android/settings/network/MobileDataPreference.kt
index d285a8c..04649d9 100644
--- a/src/com/android/settings/network/MobileDataPreference.kt
+++ b/src/com/android/settings/network/MobileDataPreference.kt
@@ -16,6 +16,7 @@
package com.android.settings.network
+import android.Manifest
import android.content.Context
import android.telephony.SubscriptionManager
import com.android.settings.R
@@ -45,11 +46,30 @@
override fun storage(context: Context): KeyValueStore = MobileDataStorage(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermissions(context: Context) =
+ arrayOf(
+ // required by TelephonyManager.isDataEnabledForReason
+ Manifest.permission.ACCESS_NETWORK_STATE,
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.READ_BASIC_PHONE_STATE,
+ )
+
+ override fun getWritePermissions(context: Context) =
+ arrayOf(
+ // required by TelephonyManager.setDataEnabledForReason
+ Manifest.permission.MODIFY_PHONE_STATE
+ )
+
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.LOW_SENSITIVITY
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index c776987..1fc9101 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -706,7 +706,7 @@
forget(mSelectedWifiEntry);
return true;
case MENU_ID_SHARE:
- WifiDppUtils.showLockScreen(getContext(),
+ WifiDppUtils.showLockScreenForWifiSharing(getContext(),
() -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
return true;
case MENU_ID_MODIFY:
diff --git a/src/com/android/settings/network/OWNERS b/src/com/android/settings/network/OWNERS
index a63a825..ad3c9da 100644
--- a/src/com/android/settings/network/OWNERS
+++ b/src/com/android/settings/network/OWNERS
@@ -1,12 +1,8 @@
# Default reviewers for this and subdirectories.
-allenwtsu@google.com
-andychou@google.com
-bonianchen@google.com
-changbetty@google.com
-leechou@google.com
+chaohuiw@google.com
+evanwu@google.com
songferngwang@google.com
tomhsu@google.com
wengsu@google.com
-zoeychen@google.com
# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/network/apn/ApnPreference.java b/src/com/android/settings/network/apn/ApnPreference.java
index 55258c1..3ba3508 100644
--- a/src/com/android/settings/network/apn/ApnPreference.java
+++ b/src/com/android/settings/network/apn/ApnPreference.java
@@ -24,31 +24,29 @@
import android.net.Uri;
import android.provider.Telephony;
import android.telephony.SubscriptionManager;
-import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioButton;
-import android.widget.RelativeLayout;
import android.widget.Toast;
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.flags.Flags;
import com.android.settings.spa.SpaActivity;
+import com.android.settingslib.widget.TwoTargetPreference;
/**
* Preference of APN UI entry
*/
-public class ApnPreference extends Preference
- implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+public class ApnPreference extends TwoTargetPreference
+ implements CompoundButton.OnCheckedChangeListener, Preference.OnPreferenceClickListener {
private static final String TAG = "ApnPreference";
private boolean mIsChecked = false;
- @Nullable
- private RadioButton mRadioButton = null;
+ private RadioButton mRadioButton;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private boolean mProtectFromCheckedChange = false;
private boolean mDefaultSelectable = true;
@@ -57,50 +55,36 @@
/**
* Constructor of Preference
*/
- public ApnPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- // Primary target and radio button could be selectable, but entire preference itself is not
- // selectable.
- setSelectable(false);
- }
-
- /**
- * Constructor of Preference
- */
- public ApnPreference(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.apnPreferenceStyle);
- }
-
- /**
- * Constructor of Preference
- */
public ApnPreference(Context context) {
- this(context, null);
+ super(context);
+ setOnPreferenceClickListener(this);
}
@Override
- public void onBindViewHolder(PreferenceViewHolder view) {
- super.onBindViewHolder(view);
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
- final RelativeLayout textArea = (RelativeLayout) view.findViewById(R.id.text_layout);
- textArea.setOnClickListener(this);
-
- final View radioButtonFrame = view.itemView.requireViewById(R.id.apn_radio_button_frame);
- final RadioButton rb = view.itemView.requireViewById(R.id.apn_radiobutton);
- mRadioButton = rb;
- if (mDefaultSelectable) {
- radioButtonFrame.setOnClickListener((v) -> {
- rb.performClick();
- });
- rb.setOnCheckedChangeListener(this);
-
- mProtectFromCheckedChange = true;
- rb.setChecked(mIsChecked);
- mProtectFromCheckedChange = false;
- radioButtonFrame.setVisibility(View.VISIBLE);
- } else {
- radioButtonFrame.setVisibility(View.GONE);
+ final RadioButton rb = (RadioButton) holder.findViewById(android.R.id.checkbox);
+ final View radioButtonFrame = holder.findViewById(android.R.id.widget_frame);
+ if (rb == null || radioButtonFrame == null) {
+ throw new RuntimeException("Failed to load system layout.");
}
+
+ mRadioButton = rb;
+ radioButtonFrame.setOnClickListener(v -> rb.performClick());
+ rb.setOnCheckedChangeListener(this);
+ setIsChecked(mIsChecked);
+ rb.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ protected boolean shouldHideSecondTarget() {
+ return !mDefaultSelectable;
+ }
+
+ @Override
+ protected int getSecondTargetResId() {
+ return R.layout.preference_widget_radiobutton;
}
/**
@@ -118,6 +102,7 @@
/**
* Change the preference status.
*/
+ @Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Log.i(TAG, "ID: " + getKey() + " :" + isChecked);
if (mProtectFromCheckedChange) {
@@ -130,19 +115,14 @@
}
@Override
- public void onClick(View layoutView) {
- super.onClick();
+ public boolean onPreferenceClick(@NonNull Preference preference) {
final Context context = getContext();
final int pos = Integer.parseInt(getKey());
- if (context == null) {
- Log.w(TAG, "No context available for pos=" + pos);
- return;
- }
if (mHideDetails) {
Toast.makeText(context, context.getString(R.string.cannot_change_apn_toast),
Toast.LENGTH_LONG).show();
- return;
+ return true;
}
final Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
@@ -156,6 +136,7 @@
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(editIntent);
}
+ return true;
}
public void setDefaultSelectable(boolean defaultSelectable) {
diff --git a/src/com/android/settings/network/telephony/SubscriptionRepository.kt b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
index 43bba07..33c1e655 100644
--- a/src/com/android/settings/network/telephony/SubscriptionRepository.kt
+++ b/src/com/android/settings/network/telephony/SubscriptionRepository.kt
@@ -40,6 +40,7 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.shareIn
+import java.util.stream.Collectors
private const val TAG = "SubscriptionRepository"
@@ -154,6 +155,18 @@
.conflate()
.flowOn(Dispatchers.Default)
+ fun removableSubscriptionInfoListFlow(): Flow<List<SubscriptionInfo>> {
+ return subscriptionsChangedFlow()
+ .map {
+ subscriptionManager.availableSubscriptionInfoList?.stream()
+ ?.filter { sub: SubscriptionInfo -> !sub.isEmbedded }
+ ?.collect(Collectors.toList()) ?: emptyList()
+ }
+ .conflate()
+ .onEach { Log.d(TAG, "getRemovableSubscriptionVisibleFlow: $it") }
+ .flowOn(Dispatchers.Default)
+ }
+
@OptIn(ExperimentalCoroutinesApi::class)
fun phoneNumberFlow(subId: Int): Flow<String?> =
activeSubscriptionInfoFlow(subId).flatMapLatest { subInfo ->
diff --git a/src/com/android/settings/network/tether/BluetoothTetherSwitchPreference.kt b/src/com/android/settings/network/tether/BluetoothTetherSwitchPreference.kt
index a22df12..a6a400b 100644
--- a/src/com/android/settings/network/tether/BluetoothTetherSwitchPreference.kt
+++ b/src/com/android/settings/network/tether/BluetoothTetherSwitchPreference.kt
@@ -77,11 +77,15 @@
return !dataSaverBackend.isDataSaverEnabled
}
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel: Int
get() = SensitivityLevel.LOW_SENSITIVITY
diff --git a/src/com/android/settings/notification/CallVolumePreference.kt b/src/com/android/settings/notification/CallVolumePreference.kt
index 031687f..0e45b23 100644
--- a/src/com/android/settings/notification/CallVolumePreference.kt
+++ b/src/com/android/settings/notification/CallVolumePreference.kt
@@ -76,10 +76,10 @@
}
}
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Int?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override val sensitivityLevel
diff --git a/src/com/android/settings/notification/MediaVolumePreference.kt b/src/com/android/settings/notification/MediaVolumePreference.kt
index a6d9c41..b644381 100644
--- a/src/com/android/settings/notification/MediaVolumePreference.kt
+++ b/src/com/android/settings/notification/MediaVolumePreference.kt
@@ -77,10 +77,10 @@
}
}
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Int?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override val sensitivityLevel
diff --git a/src/com/android/settings/notification/SeparateRingVolumePreference.kt b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
index 6a22b12..0e557c4 100644
--- a/src/com/android/settings/notification/SeparateRingVolumePreference.kt
+++ b/src/com/android/settings/notification/SeparateRingVolumePreference.kt
@@ -94,10 +94,10 @@
}
}
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Int?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(context: Context, value: Int?, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
override val sensitivityLevel
diff --git a/src/com/android/settings/regionalpreferences/ExtensionTypes.java b/src/com/android/settings/regionalpreferences/ExtensionTypes.java
index b860d29..5a2057b 100644
--- a/src/com/android/settings/regionalpreferences/ExtensionTypes.java
+++ b/src/com/android/settings/regionalpreferences/ExtensionTypes.java
@@ -25,12 +25,14 @@
public static final String FIRST_DAY_OF_WEEK = "fw";
public static final String NUMBERING_SYSTEM = "nu";
public static final String TEMPERATURE_UNIT = "mu";
+ public static final String MEASUREMENT_SYSTEM = "ms";
@StringDef({
FIRST_DAY_OF_WEEK,
CALENDAR,
TEMPERATURE_UNIT,
- NUMBERING_SYSTEM
+ NUMBERING_SYSTEM,
+ MEASUREMENT_SYSTEM
})
public @interface Values {}
}
diff --git a/src/com/android/settings/regionalpreferences/MeasurementSystemController.java b/src/com/android/settings/regionalpreferences/MeasurementSystemController.java
new file mode 100644
index 0000000..1b6daaf
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/MeasurementSystemController.java
@@ -0,0 +1,68 @@
+/**
+ * Copyright (C) 2024 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.os.LocaleList;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.flags.Flags;
+
+import java.util.Locale;
+
+/** A controller for the entry of measurement system page */
+public class MeasurementSystemController extends BasePreferenceController {
+ private static final String TAG = "MeasurementSystemController";
+ public MeasurementSystemController(@NonNull Context context, @NonNull String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (Flags.regionalPreferencesApiEnabled()) {
+ return AVAILABLE;
+ }
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ @NonNull
+ public CharSequence getSummary() {
+ LocaleList localeList = LocaleList.getDefault();
+ Locale locale = localeList.get(0);
+ return getMeasurementSystem(locale);
+ }
+
+ private String getMeasurementSystem(Locale locale) {
+ String type = locale.getUnicodeLocaleType(
+ RegionalPreferencesDataUtils.EXTENSION_TYPE_MEASUREMENT_SYSTEM);
+ if (type != null) {
+ if (type.equals(RegionalPreferencesDataUtils.MEASUREMENT_SYSTEM_METRIC)) {
+ return mContext.getString(R.string.metric_measurement_system);
+ }
+ if (type.equals(RegionalPreferencesDataUtils.MEASUREMENT_SYSTEM_UK)) {
+ return mContext.getString(R.string.uk_measurement_system);
+ }
+ return mContext.getString(R.string.us_measurement_system);
+ } else {
+ return mContext.getString(R.string.default_string_of_regional_preference);
+ }
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/MeasurementSystemItemCategoryController.java b/src/com/android/settings/regionalpreferences/MeasurementSystemItemCategoryController.java
new file mode 100644
index 0000000..03b8817
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/MeasurementSystemItemCategoryController.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (C) 2024 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.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.widget.PreferenceCategoryController;
+
+/** Category preference controller for measurement system preferences. */
+public class MeasurementSystemItemCategoryController extends PreferenceCategoryController {
+
+ private static final String LOG_TAG = "MeasurementSystemItemCategoryController";
+ private static final String KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM =
+ "measurement_system_item_category";
+ private static final String KEY_PREFERENCE_MEASUREMENT_SYSTEM_ITEM =
+ "measurement_system_item_list";
+
+ public MeasurementSystemItemCategoryController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(@NonNull PreferenceScreen screen) {
+ super.displayPreference(screen);
+ PreferenceCategory preferenceCategory =
+ screen.findPreference(KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM);
+ if (preferenceCategory == null) {
+ Log.d(LOG_TAG, "displayPreference(), Can not find the category.");
+ return;
+ }
+ preferenceCategory.setVisible(isAvailable());
+ MeasurementSystemItemListController measurementSystemItemListController =
+ new MeasurementSystemItemListController(
+ mContext,
+ KEY_PREFERENCE_MEASUREMENT_SYSTEM_ITEM);
+ measurementSystemItemListController.displayPreference(screen);
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/MeasurementSystemItemFragment.java b/src/com/android/settings/regionalpreferences/MeasurementSystemItemFragment.java
new file mode 100644
index 0000000..231a34e
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/MeasurementSystemItemFragment.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2024 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.app.settings.SettingsEnums;
+import android.content.Context;
+
+import com.android.settings.R;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.flags.Flags;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.search.SearchIndexable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Main fragment to display measurement system. */
+@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
+public class MeasurementSystemItemFragment extends DashboardFragment {
+
+ private static final String LOG_TAG = "MeasurementSystemItemFragment";
+ private static final String KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM =
+ "measurement_system_item_category";
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.regional_preferences_measurement_system;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.MEASUREMENT_SYSTEM_PREFERENCE;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return LOG_TAG;
+ }
+
+ @Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ controllers.add(new MeasurementSystemItemCategoryController(context,
+ KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM));
+ return controllers;
+ }
+
+ public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+ new BaseSearchIndexProvider(R.xml.regional_preferences_measurement_system) {
+ @Override
+ protected boolean isPageSearchEnabled(Context context) {
+ if (!Flags.regionalPreferencesApiEnabled()) {
+ return false;
+ }
+ return true;
+ }
+ };
+}
diff --git a/src/com/android/settings/regionalpreferences/MeasurementSystemItemListController.java b/src/com/android/settings/regionalpreferences/MeasurementSystemItemListController.java
new file mode 100644
index 0000000..061c4f8
--- /dev/null
+++ b/src/com/android/settings/regionalpreferences/MeasurementSystemItemListController.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (C) 2024 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.app.settings.SettingsEnums;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.settings.R;
+
+/** A controller for handling all measurement system preferences. */
+public class MeasurementSystemItemListController extends
+ RegionalPreferenceListBasePreferenceController {
+
+ private static final String KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM =
+ "measurement_system_item_category";
+ private static final String KEY_PREFERENCE_MEASUREMENT_SYSTEM_ITEM =
+ "measurement_system_item_list";
+
+ public MeasurementSystemItemListController(@NonNull Context context, @NonNull String key) {
+ super(context, key);
+ }
+
+ @Override
+ protected String getPreferenceTitle(String item) {
+ return RegionalPreferencesDataUtils.measurementSystemConverter(mContext, item);
+ }
+
+ @Override
+ protected String getPreferenceCategoryKey() {
+ return KEY_PREFERENCE_CATEGORY_MEASUREMENT_SYSTEM_ITEM;
+ }
+
+ @Override
+ @NonNull
+ public String getPreferenceKey() {
+ return KEY_PREFERENCE_MEASUREMENT_SYSTEM_ITEM;
+ }
+
+ @Override
+ protected String getExtensionTypes() {
+ return ExtensionTypes.MEASUREMENT_SYSTEM;
+ }
+
+ @Override
+ protected String[] getUnitValues() {
+ return mContext.getResources().getStringArray(R.array.measurement_system);
+ }
+
+ @Override
+ protected int getMetricsActionKey() {
+ return SettingsEnums.ACTION_SET_MEASUREMENT_SYSTEM;
+ }
+}
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
index dda0579..606307f 100644
--- a/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferenceListBasePreferenceController.java
@@ -65,8 +65,8 @@
RegionalPreferencesDataUtils.savePreference(mContext, getExtensionTypes(),
item.equals(RegionalPreferencesDataUtils.DEFAULT_VALUE)
? null : item);
- String metrics =
- getMetricsActionKey() == SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK ? ""
+ String metrics = shouldUseEmptyMetrics()
+ ? ""
: getPreferenceTitle(value) + " > " + getPreferenceTitle(item);
mMetricsFeatureProvider.action(mContext, getMetricsActionKey(), metrics);
});
@@ -79,6 +79,14 @@
return AVAILABLE;
}
+ private boolean shouldUseEmptyMetrics() {
+ if (getMetricsActionKey() == SettingsEnums.ACTION_SET_FIRST_DAY_OF_WEEK
+ || getMetricsActionKey() == SettingsEnums.ACTION_SET_MEASUREMENT_SYSTEM) {
+ return true;
+ }
+ return false;
+ }
+
protected abstract String getPreferenceTitle(String item);
protected abstract String getPreferenceCategoryKey();
diff --git a/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java b/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java
index d1ae40b..669bcdd 100644
--- a/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java
+++ b/src/com/android/settings/regionalpreferences/RegionalPreferencesDataUtils.java
@@ -17,6 +17,7 @@
package com.android.settings.regionalpreferences;
import android.content.Context;
+import android.icu.util.LocaleData;
import android.icu.util.ULocale;
import android.os.LocaleList;
import android.provider.Settings;
@@ -32,6 +33,10 @@
/** Provides utils for regional preferences. */
public class RegionalPreferencesDataUtils {
static final String DEFAULT_VALUE = "default";
+ static final String EXTENSION_TYPE_MEASUREMENT_SYSTEM = "ms";
+ static final String MEASUREMENT_SYSTEM_METRIC = "metric";
+ static final String MEASUREMENT_SYSTEM_UK = "uksystem";
+ static final String MEASUREMENT_SYSTEM_US = "ussystem";
static String getDefaultUnicodeExtensionData(Context contxt, String type) {
// 1. Check cache data in Settings provider.
@@ -118,4 +123,30 @@
return context.getString(R.string.default_string_of_regional_preference);
}
}
+
+ static String measurementSystemConverter(Context context, String unit) {
+ switch (unit) {
+ case MEASUREMENT_SYSTEM_METRIC:
+ return context.getString(R.string.metric_measurement_system);
+ case MEASUREMENT_SYSTEM_UK:
+ return context.getString(R.string.uk_measurement_system);
+ case MEASUREMENT_SYSTEM_US:
+ return context.getString(R.string.us_measurement_system);
+ default:
+ return context.getString(R.string.default_string_of_regional_preference);
+ }
+ }
+
+ static String getDefaultMeasurementSystem() {
+ LocaleList localeList = LocaleList.getDefault();
+ Locale locale = localeList.get(0);
+ ULocale uLocale = ULocale.forLocale(locale);
+ if (LocaleData.getMeasurementSystem(uLocale) == LocaleData.MeasurementSystem.SI) {
+ return RegionalPreferencesDataUtils.MEASUREMENT_SYSTEM_METRIC;
+ }
+ if (LocaleData.getMeasurementSystem(uLocale) == LocaleData.MeasurementSystem.UK) {
+ return RegionalPreferencesDataUtils.MEASUREMENT_SYSTEM_UK;
+ }
+ return RegionalPreferencesDataUtils.MEASUREMENT_SYSTEM_US;
+ }
}
diff --git a/src/com/android/settings/service/PreferenceService.kt b/src/com/android/settings/service/PreferenceService.kt
index 3a67762..81ab584 100644
--- a/src/com/android/settings/service/PreferenceService.kt
+++ b/src/com/android/settings/service/PreferenceService.kt
@@ -16,9 +16,9 @@
package com.android.settings.service
+import android.app.Application
import android.os.Binder
import android.os.OutcomeReceiver
-import android.os.Process
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
import android.service.settings.preferences.MetadataRequest
@@ -26,43 +26,60 @@
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService
+import com.android.settingslib.graph.GetPreferenceGraphApiHandler
+import com.android.settingslib.graph.GetPreferenceGraphRequest
import com.android.settingslib.graph.PreferenceGetterApiHandler
+import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceSetterApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
-import java.lang.Exception
class PreferenceService : SettingsPreferenceService() {
- private val scope = CoroutineScope(Job() + Dispatchers.Main)
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())
+ private val graphApi = GraphProvider(3)
override fun onGetAllPreferenceMetadata(
request: MetadataRequest,
- callback: OutcomeReceiver<MetadataResult, Exception>
+ callback: OutcomeReceiver<MetadataResult, Exception>,
) {
- // TODO(379750656): Update graph API to be usable outside SettingsLib
- callback.onError(UnsupportedOperationException("Not yet supported"))
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ scope.launch {
+ val graphProto =
+ graphApi.invoke(
+ application,
+ callingPid,
+ callingUid,
+ GetPreferenceGraphRequest(
+ includeValue = false,
+ flags = PreferenceGetterFlags.METADATA,
+ ),
+ )
+ val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
+ callback.onResult(result)
+ }
}
override fun onGetPreferenceValue(
request: GetValueRequest,
- callback: OutcomeReceiver<GetValueResult, Exception>
+ callback: OutcomeReceiver<GetValueResult, Exception>,
) {
- scope.launch(Dispatchers.IO) {
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ scope.launch {
val apiRequest = transformFrameworkGetValueRequest(request)
- val response = getApiHandler.invoke(application, Process.myUid(),
- Binder.getCallingPid(), apiRequest)
- val result = transformCatalystGetValueResponse(
- this@PreferenceService,
- request,
- response
- )
+ val response = getApiHandler.invoke(application, callingPid, callingUid, apiRequest)
+ val result =
+ transformCatalystGetValueResponse(this@PreferenceService, request, response)
if (result == null) {
callback.onError(IllegalStateException("No response"))
} else {
@@ -73,20 +90,32 @@
override fun onSetPreferenceValue(
request: SetValueRequest,
- callback: OutcomeReceiver<SetValueResult, Exception>
+ callback: OutcomeReceiver<SetValueResult, Exception>,
) {
- scope.launch(Dispatchers.IO) {
+ // MUST get pid/uid in binder thread
+ val callingPid = Binder.getCallingPid()
+ val callingUid = Binder.getCallingUid()
+ scope.launch {
val apiRequest = transformFrameworkSetValueRequest(request)
if (apiRequest == null) {
callback.onResult(
SetValueResult.Builder(SetValueResult.RESULT_INVALID_REQUEST).build()
)
} else {
- val response = setApiHandler.invoke(application, Process.myUid(),
- Binder.getCallingPid(), apiRequest)
+ val response = setApiHandler.invoke(application, callingPid, callingUid, apiRequest)
callback.onResult(transformCatalystSetValueResponse(response))
}
}
}
+
+ // Basic implementation - we already have permission to access Graph for Metadata via superclass
+ private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
+ override fun hasPermission(
+ application: Application,
+ callingPid: Int,
+ callingUid: Int,
+ request: GetPreferenceGraphRequest,
+ ) = true
+ }
}
diff --git a/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt b/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
index 7a4c7fc..2fe2754 100644
--- a/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
+++ b/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -34,9 +35,55 @@
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.getText
+import com.android.settingslib.graph.proto.PreferenceGraphProto
+import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.toIntent
import com.android.settingslib.metadata.SensitivityLevel
+/** Transform Catalyst Graph result to Framework GET METADATA result */
+fun transformCatalystGetMetadataResponse(
+ context: Context,
+ graph: PreferenceGraphProto
+): MetadataResult {
+ val preferences = mutableSetOf<PreferenceWithScreen>()
+ // recursive function to visit all nodes in preference group
+ fun traverseGroupOrPref(
+ screenKey: String,
+ groupOrPref: PreferenceOrGroupProto,
+ ) {
+ when (groupOrPref.kindCase) {
+ PreferenceOrGroupProto.KindCase.PREFERENCE ->
+ preferences.add(
+ PreferenceWithScreen(screenKey, groupOrPref.preference)
+ )
+ PreferenceOrGroupProto.KindCase.GROUP -> {
+ for (child in groupOrPref.group.preferencesList) {
+ traverseGroupOrPref(screenKey, child)
+ }
+ }
+ else -> {}
+ }
+ }
+ // traverse all screens and all preferences on screen
+ for ((screenKey, screen) in graph.screensMap) {
+ for (groupOrPref in screen.root.preferencesList) {
+ traverseGroupOrPref(screenKey, groupOrPref)
+ }
+ }
+
+ return if (preferences.isNotEmpty()) {
+ MetadataResult.Builder(MetadataResult.RESULT_OK)
+ .setMetadataList(
+ preferences.map {
+ it.preference.toMetadata(context, it.screenKey)
+ }
+ )
+ .build()
+ } else {
+ MetadataResult.Builder(MetadataResult.RESULT_UNSUPPORTED).build()
+ }
+}
+
/** Translate Framework GET VALUE request to Catalyst GET VALUE request */
fun transformFrameworkGetValueRequest(
request: GetValueRequest,
@@ -133,6 +180,11 @@
return SetValueResult.Builder(resultCode).build()
}
+private data class PreferenceWithScreen(
+ val screenKey: String,
+ val preference: PreferenceProto,
+)
+
private fun PreferenceProto.toMetadata(
context: Context,
screenKey: String
@@ -140,7 +192,7 @@
val sensitivity = when (sensitivityLevel) {
SensitivityLevel.NO_SENSITIVITY -> SettingsPreferenceMetadata.NO_SENSITIVITY
SensitivityLevel.LOW_SENSITIVITY -> SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION
- SensitivityLevel.MEDIUM_SENSITIVITY -> SettingsPreferenceMetadata.EXPECT_PRE_CONFIRMATION
+ SensitivityLevel.MEDIUM_SENSITIVITY -> SettingsPreferenceMetadata.DEEPLINK_ONLY
else -> SettingsPreferenceMetadata.NO_DIRECT_ACCESS
}
return SettingsPreferenceMetadata.Builder(screenKey, key)
@@ -152,5 +204,7 @@
.setWritable(persistent)
.setLaunchIntent(launchIntent.toIntent())
.setWriteSensitivity(sensitivity)
+ .setReadPermissions(readPermissionsList)
+ .setWritePermissions(writePermissionsList)
.build()
}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
deleted file mode 100644
index f808924..0000000
--- a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright (C) 2020 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.sim.receivers;
-
-import static android.content.Context.MODE_PRIVATE;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.SubscriptionInfo;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.telephony.UiccCardInfo;
-import android.telephony.UiccPortInfo;
-import android.telephony.UiccSlotInfo;
-import android.util.Log;
-
-import com.android.settings.flags.Flags;
-import com.android.settings.network.SubscriptionUtil;
-import com.android.settings.network.UiccSlotUtil;
-import com.android.settings.network.UiccSlotsException;
-import com.android.settings.sim.ChooseSimActivity;
-import com.android.settings.sim.DsdsDialogActivity;
-import com.android.settings.sim.SimActivationNotifier;
-import com.android.settings.sim.SimNotificationService;
-import com.android.settings.sim.SwitchToEsimConfirmDialogActivity;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import javax.annotation.Nullable;
-
-/** Perform actions after a slot change event is triggered. */
-public class SimSlotChangeHandler {
- private static final String TAG = "SimSlotChangeHandler";
-
- private static final String EUICC_PREFS = "euicc_prefs";
- // Shared preference keys
- private static final String KEY_REMOVABLE_SLOT_STATE = "removable_slot_state";
- private static final String KEY_SUW_PSIM_ACTION = "suw_psim_action";
- // User's last removable SIM insertion / removal action during SUW.
- private static final int LAST_USER_ACTION_IN_SUW_NONE = 0;
- private static final int LAST_USER_ACTION_IN_SUW_INSERT = 1;
- private static final int LAST_USER_ACTION_IN_SUW_REMOVE = 2;
-
- private static volatile SimSlotChangeHandler sSlotChangeHandler;
-
- /** Returns a SIM slot change handler singleton. */
- public static SimSlotChangeHandler get() {
- if (sSlotChangeHandler == null) {
- synchronized (SimSlotChangeHandler.class) {
- if (sSlotChangeHandler == null) {
- sSlotChangeHandler = new SimSlotChangeHandler();
- }
- }
- }
- return sSlotChangeHandler;
- }
-
- private SubscriptionManager mSubMgr;
- private TelephonyManager mTelMgr;
- private Context mContext;
-
- void onSlotsStatusChange(Context context) {
- init(context);
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new IllegalStateException("Cannot be called from main thread.");
- }
-
- UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
- if (removableSlotInfo == null) {
- Log.e(TAG, "Unable to find the removable slot. Do nothing.");
- return;
- }
- Log.i(TAG, "The removableSlotInfo: " + removableSlotInfo);
- int lastRemovableSlotState = getLastRemovableSimSlotState(mContext);
- int currentRemovableSlotState = removableSlotInfo.getCardStateInfo();
- Log.d(TAG,
- "lastRemovableSlotState: " + lastRemovableSlotState + ",currentRemovableSlotState: "
- + currentRemovableSlotState);
- boolean isRemovableSimInserted =
- lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
- && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT;
- boolean isRemovableSimRemoved =
- lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
- && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT;
-
- // Sets the current removable slot state.
- setRemovableSimSlotState(mContext, currentRemovableSlotState);
-
- if (mTelMgr.getActiveModemCount() > 1) {
- if (!isRemovableSimInserted) {
- Log.d(TAG, "Removable Sim is not inserted in DSDS mode. Do nothing.");
- return;
- }
-
- if (Flags.isDualSimOnboardingEnabled()) {
- // ForNewUi, when the user inserts the psim, showing the sim onboarding for the user
- // to setup the sim switching or the default data subscription in DSDS.
- // Will show dialog for below case.
- // 1. the psim slot is not active.
- // 2. there are one or more active sim.
- handleRemovableSimInsertWhenDsds(removableSlotInfo);
- return;
- } else if (!isMultipleEnabledProfilesSupported()) {
- Log.d(TAG, "The device is already in DSDS mode and no MEP. Do nothing.");
- return;
- } else if (isMultipleEnabledProfilesSupported()) {
- handleRemovableSimInsertUnderDsdsMep(removableSlotInfo);
- return;
- }
- }
-
- if (isRemovableSimInserted) {
- handleSimInsert(removableSlotInfo);
- return;
- }
- if (isRemovableSimRemoved) {
- handleSimRemove(removableSlotInfo);
- return;
- }
- Log.i(TAG, "Do nothing on slot status changes.");
- }
-
- void onSuwFinish(Context context) {
- init(context);
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- throw new IllegalStateException("Cannot be called from main thread.");
- }
-
- if (mTelMgr.getActiveModemCount() > 1) {
- Log.i(TAG, "The device is already in DSDS mode. Do nothing.");
- return;
- }
-
- UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
- if (removableSlotInfo == null) {
- Log.e(TAG, "Unable to find the removable slot. Do nothing.");
- return;
- }
-
- boolean embeddedSimExist = getGroupedEmbeddedSubscriptions().size() != 0;
- int removableSlotAction = getSuwRemovableSlotAction(mContext);
- setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_NONE);
-
- if (embeddedSimExist
- && removableSlotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_PRESENT) {
- if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
- Log.i(TAG, "DSDS condition satisfied. Show notification.");
- SimNotificationService.scheduleSimNotification(
- mContext, SimActivationNotifier.NotificationType.ENABLE_DSDS);
- } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_INSERT) {
- Log.i(
- TAG,
- "Both removable SIM and eSIM are present. DSDS condition doesn't"
- + " satisfied. User inserted pSIM during SUW. Show choose SIM"
- + " screen.");
- startChooseSimActivity(true);
- }
- } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
- handleSimRemove(removableSlotInfo);
- }
- }
-
- private void init(Context context) {
- mSubMgr =
- (SubscriptionManager)
- context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
- mTelMgr = context.getSystemService(TelephonyManager.class);
- mContext = context;
- }
-
- private void handleSimInsert(UiccSlotInfo removableSlotInfo) {
- Log.i(TAG, "Handle SIM inserted.");
- if (!isSuwFinished(mContext)) {
- Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished");
- setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_INSERT);
- return;
- }
- if (removableSlotInfo.getPorts().stream().findFirst().get().isActive()) {
- Log.i(TAG, "The removable slot is already active. Do nothing.");
- return;
- }
-
- if (hasActiveEsimSubscription()) {
- if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
- Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.");
- if (Flags.isDualSimOnboardingEnabled()) {
- // enable dsds by sim onboarding flow
- handleRemovableSimInsertWhenDsds(removableSlotInfo);
- } else {
- startDsdsDialogActivity();
- }
- } else {
- Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.");
- startChooseSimActivity(true);
- }
- return;
- }
-
- Log.i(
- TAG,
- "No enabled eSIM profile. Ready to switch to removable slot and show"
- + " notification.");
- try {
- UiccSlotUtil.switchToRemovableSlot(
- UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext.getApplicationContext());
- } catch (UiccSlotsException e) {
- Log.e(TAG, "Failed to switch to removable slot.");
- return;
- }
- SimNotificationService.scheduleSimNotification(
- mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT);
- }
-
- private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
- Log.i(TAG, "Handle SIM removed.");
-
- if (!isSuwFinished(mContext)) {
- Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished");
- setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_REMOVE);
- return;
- }
-
- List<SubscriptionInfo> groupedEmbeddedSubscriptions = getGroupedEmbeddedSubscriptions();
- if (groupedEmbeddedSubscriptions.size() == 0 || !removableSlotInfo.getPorts().stream()
- .findFirst().get().isActive()) {
- Log.i(TAG, "eSIM slot is active or no subscriptions exist. Do nothing."
- + " The removableSlotInfo: " + removableSlotInfo
- + ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions);
- return;
- }
-
- // If there is only 1 eSIM profile exists, we ask the user if they want to switch to that
- // profile.
- if (groupedEmbeddedSubscriptions.size() == 1) {
- Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.");
- startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions.get(0));
- return;
- }
-
- // If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
- // the number they want to use.
- Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.");
- startChooseSimActivity(false);
- }
-
- private boolean hasOtherActiveSubInfo(int psimSubId) {
- List<SubscriptionInfo> activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr);
- return activeSubs.stream()
- .anyMatch(subscriptionInfo -> subscriptionInfo.getSubscriptionId() != psimSubId);
- }
-
- private boolean hasAnyPortActiveInSlot(UiccSlotInfo removableSlotInfo) {
- return removableSlotInfo.getPorts().stream().anyMatch(UiccPortInfo::isActive);
- }
-
- private void handleRemovableSimInsertWhenDsds(UiccSlotInfo removableSlotInfo) {
- Log.i(TAG, "ForNewUi: Handle Removable SIM inserted");
- List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
- if (subscriptionInfos.isEmpty()) {
- Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
- return;
- }
- Log.d(TAG, "getAvailableRemovableSubscription:" + subscriptionInfos);
- int psimSubId = subscriptionInfos.get(0).getSubscriptionId();
- if (!hasAnyPortActiveInSlot(removableSlotInfo) || hasOtherActiveSubInfo(psimSubId)) {
- Log.d(TAG, "ForNewUi Start Setup flow");
- startSimConfirmDialogActivity(psimSubId);
- }
- }
-
- private void handleRemovableSimInsertUnderDsdsMep(UiccSlotInfo removableSlotInfo) {
- Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.");
-
- if (removableSlotInfo.getPorts().stream().findFirst().get().isActive()) {
- Log.i(TAG, "The removable slot is already active. Do nothing. removableSlotInfo: "
- + removableSlotInfo);
- return;
- }
-
- List<SubscriptionInfo> subscriptionInfos = getAvailableRemovableSubscription();
- if (subscriptionInfos.isEmpty()) {
- Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.");
- return;
- }
- Log.d(TAG, "getAvailableRemovableSubscription:" + subscriptionInfos);
- startSimConfirmDialogActivity(subscriptionInfos.get(0).getSubscriptionId());
- }
-
- private int getLastRemovableSimSlotState(Context context) {
- final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
- return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT);
- }
-
- 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) {
- final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
- return prefs.getInt(KEY_SUW_PSIM_ACTION, LAST_USER_ACTION_IN_SUW_NONE);
- }
-
- private void setSuwRemovableSlotAction(Context context, int action) {
- final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
- prefs.edit().putInt(KEY_SUW_PSIM_ACTION, action).apply();
- }
-
- @Nullable
- private UiccSlotInfo getRemovableUiccSlotInfo() {
- UiccSlotInfo[] slotInfos = mTelMgr.getUiccSlotsInfo();
- if (slotInfos == null) {
- Log.e(TAG, "slotInfos is null. Unable to get slot infos.");
- return null;
- }
- for (UiccSlotInfo slotInfo : slotInfos) {
- if (slotInfo != null && slotInfo.isRemovable()) {
- return slotInfo;
- }
- }
- return null;
- }
-
- private static boolean isSuwFinished(Context context) {
- try {
- // DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
- return Settings.Global.getInt(
- context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED)
- == 1;
- } catch (Settings.SettingNotFoundException e) {
- Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e);
- return false;
- }
- }
-
- private boolean hasActiveEsimSubscription() {
- List<SubscriptionInfo> activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr);
- return activeSubs.stream().anyMatch(SubscriptionInfo::isEmbedded);
- }
-
- private List<SubscriptionInfo> getGroupedEmbeddedSubscriptions() {
- List<SubscriptionInfo> groupedSubscriptions =
- SubscriptionUtil.getSelectableSubscriptionInfoList(mContext);
- if (groupedSubscriptions == null) {
- return ImmutableList.of();
- }
- return ImmutableList.copyOf(
- groupedSubscriptions.stream()
- .filter(sub -> sub.isEmbedded())
- .collect(Collectors.toList()));
- }
-
- protected List<SubscriptionInfo> getAvailableRemovableSubscription() {
- 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) {
- Intent intent = ChooseSimActivity.getIntent(mContext);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted);
- mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
- }
-
- private void startSwitchSlotConfirmDialogActivity(SubscriptionInfo subscriptionInfo) {
- Intent intent = new Intent(mContext, SwitchToEsimConfirmDialogActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo);
- mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
- }
-
- private void startDsdsDialogActivity() {
- Intent intent = new Intent(mContext, DsdsDialogActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
- }
-
- private void startSimConfirmDialogActivity(int subId) {
- if (!isSuwFinished(mContext)) {
- Log.d(TAG, "Still in SUW. Do nothing");
- return;
- }
- if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
- Log.i(TAG, "Unable to enable subscription due to invalid subscription ID.");
- return;
- }
- Log.d(TAG, "Start ToggleSubscriptionDialogActivity with " + subId + " under DSDS+Mep.");
- SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true, true);
- }
-
- private boolean isMultipleEnabledProfilesSupported() {
- List<UiccCardInfo> cardInfos = mTelMgr.getUiccCardsInfo();
- if (cardInfos == null) {
- Log.d(TAG, "UICC cards info list is empty.");
- return false;
- }
- return cardInfos.stream().anyMatch(
- cardInfo -> cardInfo.isMultipleEnabledProfilesSupported());
- }
-
- private SimSlotChangeHandler() {}
-}
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.kt b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.kt
new file mode 100644
index 0000000..940184a
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.kt
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2020 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.sim.receivers
+
+import android.content.Context
+import android.content.Intent
+import android.os.Looper
+import android.os.UserHandle
+import android.provider.Settings
+import android.provider.Settings.SettingNotFoundException
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.UiccCardInfo
+import android.telephony.UiccSlotInfo
+import android.util.Log
+import com.android.settings.flags.Flags
+import com.android.settings.network.SubscriptionUtil
+import com.android.settings.network.UiccSlotUtil
+import com.android.settings.network.UiccSlotsException
+import com.android.settings.network.telephony.SubscriptionRepository
+import com.android.settings.sim.ChooseSimActivity
+import com.android.settings.sim.DsdsDialogActivity
+import com.android.settings.sim.SimActivationNotifier
+import com.android.settings.sim.SimNotificationService
+import com.android.settings.sim.SwitchToEsimConfirmDialogActivity
+import com.google.common.collect.ImmutableList
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+import java.util.stream.Collectors
+import kotlin.concurrent.Volatile
+
+/** Perform actions after a slot change event is triggered. */
+class SimSlotChangeHandler private constructor() {
+ private var mSubMgr: SubscriptionManager? = null
+ private var mTelMgr: TelephonyManager? = null
+ private var mContext: Context? = null
+
+ fun onSlotsStatusChange(context: Context) {
+ init(context)
+
+ check(Looper.myLooper() != Looper.getMainLooper()) { "Cannot be called from main thread." }
+
+ val removableSlotInfo = removableUiccSlotInfo
+ if (removableSlotInfo == null) {
+ Log.e(TAG, "Unable to find the removable slot. Do nothing.")
+ return
+ }
+ Log.i(
+ TAG,
+ "The removableSlotInfo: $removableSlotInfo"
+ )
+ val lastRemovableSlotState = getLastRemovableSimSlotState(mContext!!)
+ val currentRemovableSlotState = removableSlotInfo.cardStateInfo
+ Log.d(
+ TAG,
+ ("lastRemovableSlotState: " + lastRemovableSlotState + ",currentRemovableSlotState: "
+ + currentRemovableSlotState)
+ )
+ val isRemovableSimInserted =
+ lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
+ && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+ val isRemovableSimRemoved =
+ lastRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+ && currentRemovableSlotState == UiccSlotInfo.CARD_STATE_INFO_ABSENT
+
+ // Sets the current removable slot state.
+ setRemovableSimSlotState(mContext!!, currentRemovableSlotState)
+
+ if (mTelMgr!!.activeModemCount > 1) {
+ if (!isRemovableSimInserted) {
+ Log.d(TAG, "Removable Sim is not inserted in DSDS mode. Do nothing.")
+ return
+ }
+
+ if (Flags.isDualSimOnboardingEnabled()) {
+ // ForNewUi, when the user inserts the psim, showing the sim onboarding for the user
+ // to setup the sim switching or the default data subscription in DSDS.
+ // Will show dialog for below case.
+ // 1. the psim slot is not active.
+ // 2. there are one or more active sim.
+ handleRemovableSimInsertWhenDsds(removableSlotInfo)
+ return
+ } else if (!isMultipleEnabledProfilesSupported) {
+ Log.d(TAG, "The device is already in DSDS mode and no MEP. Do nothing.")
+ return
+ } else if (isMultipleEnabledProfilesSupported) {
+ handleRemovableSimInsertUnderDsdsMep(removableSlotInfo)
+ return
+ }
+ }
+
+ if (isRemovableSimInserted) {
+ handleSimInsert(removableSlotInfo)
+ return
+ }
+ if (isRemovableSimRemoved) {
+ handleSimRemove(removableSlotInfo)
+ return
+ }
+ Log.i(TAG, "Do nothing on slot status changes.")
+ }
+
+ fun onSuwFinish(context: Context) {
+ init(context)
+
+ check(Looper.myLooper() != Looper.getMainLooper()) { "Cannot be called from main thread." }
+
+ if (mTelMgr!!.activeModemCount > 1) {
+ Log.i(TAG, "The device is already in DSDS mode. Do nothing.")
+ return
+ }
+
+ val removableSlotInfo = removableUiccSlotInfo
+ if (removableSlotInfo == null) {
+ Log.e(TAG, "Unable to find the removable slot. Do nothing.")
+ return
+ }
+
+ val embeddedSimExist = groupedEmbeddedSubscriptions.size != 0
+ val removableSlotAction = getSuwRemovableSlotAction(mContext!!)
+ setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_NONE)
+
+ if (embeddedSimExist
+ && removableSlotInfo.cardStateInfo == UiccSlotInfo.CARD_STATE_INFO_PRESENT
+ ) {
+ if (mTelMgr!!.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
+ Log.i(TAG, "DSDS condition satisfied. Show notification.")
+ SimNotificationService.scheduleSimNotification(
+ mContext, SimActivationNotifier.NotificationType.ENABLE_DSDS
+ )
+ } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_INSERT) {
+ Log.i(
+ TAG,
+ ("Both removable SIM and eSIM are present. DSDS condition doesn't"
+ + " satisfied. User inserted pSIM during SUW. Show choose SIM"
+ + " screen.")
+ )
+ startChooseSimActivity(true)
+ }
+ } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
+ handleSimRemove(removableSlotInfo)
+ }
+ }
+
+ private fun init(context: Context) {
+ mSubMgr =
+ context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
+ mTelMgr = context.getSystemService(TelephonyManager::class.java)
+ mContext = context
+ }
+
+ private fun handleSimInsert(removableSlotInfo: UiccSlotInfo) {
+ Log.i(TAG, "Handle SIM inserted.")
+ if (!isSuwFinished(mContext!!)) {
+ Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished")
+ setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_INSERT)
+ return
+ }
+ if (removableSlotInfo.ports.stream().findFirst().get().isActive) {
+ Log.i(TAG, "The removable slot is already active. Do nothing.")
+ return
+ }
+
+ if (hasActiveEsimSubscription()) {
+ if (mTelMgr!!.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
+ Log.i(TAG, "Enabled profile exists. DSDS condition satisfied.")
+ if (Flags.isDualSimOnboardingEnabled()) {
+ // enable dsds by sim onboarding flow
+ handleRemovableSimInsertWhenDsds(removableSlotInfo)
+ } else {
+ startDsdsDialogActivity()
+ }
+ } else {
+ Log.i(TAG, "Enabled profile exists. DSDS condition not satisfied.")
+ startChooseSimActivity(true)
+ }
+ return
+ }
+
+ Log.i(
+ TAG,
+ "No enabled eSIM profile. Ready to switch to removable slot and show"
+ + " notification."
+ )
+ try {
+ UiccSlotUtil.switchToRemovableSlot(
+ UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID, mContext!!.applicationContext
+ )
+ } catch (e: UiccSlotsException) {
+ Log.e(TAG, "Failed to switch to removable slot.")
+ return
+ }
+ SimNotificationService.scheduleSimNotification(
+ mContext, SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT
+ )
+ }
+
+ private fun handleSimRemove(removableSlotInfo: UiccSlotInfo) {
+ Log.i(TAG, "Handle SIM removed.")
+
+ if (!isSuwFinished(mContext!!)) {
+ Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished")
+ setSuwRemovableSlotAction(mContext!!, LAST_USER_ACTION_IN_SUW_REMOVE)
+ return
+ }
+
+ val groupedEmbeddedSubscriptions =
+ groupedEmbeddedSubscriptions
+ if (groupedEmbeddedSubscriptions.isEmpty() || !removableSlotInfo.ports.stream()
+ .findFirst().get().isActive
+ ) {
+ Log.i(
+ TAG, ("eSIM slot is active or no subscriptions exist. Do nothing."
+ + " The removableSlotInfo: " + removableSlotInfo
+ + ", groupedEmbeddedSubscriptions: " + groupedEmbeddedSubscriptions)
+ )
+ return
+ }
+
+ // If there is only 1 eSIM profile exists, we ask the user if they want to switch to that
+ // profile.
+ if (groupedEmbeddedSubscriptions.size == 1) {
+ Log.i(TAG, "Only 1 eSIM profile found. Ask user's consent to switch.")
+ startSwitchSlotConfirmDialogActivity(groupedEmbeddedSubscriptions[0])
+ return
+ }
+
+ // If there are more than 1 eSIM profiles installed, we show a screen to let users to choose
+ // the number they want to use.
+ Log.i(TAG, "Multiple eSIM profiles found. Ask user which subscription to use.")
+ startChooseSimActivity(false)
+ }
+
+ private fun hasOtherActiveSubInfo(psimSubId: Int): Boolean {
+ val activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr)
+ return activeSubs.stream()
+ .anyMatch { subscriptionInfo -> subscriptionInfo.subscriptionId != psimSubId }
+ }
+
+ private fun hasAnyPortActiveInSlot(removableSlotInfo: UiccSlotInfo): Boolean {
+ return removableSlotInfo.ports.stream().anyMatch { slot -> slot.isActive }
+ }
+
+ private fun handleRemovableSimInsertWhenDsds(removableSlotInfo: UiccSlotInfo) {
+ Log.i(TAG, "ForNewUi: Handle Removable SIM inserted")
+ CoroutineScope(Dispatchers.Default + SupervisorJob()).launch {
+ withContext(Dispatchers.Default) {
+ val subscriptionInfos =
+ withTimeoutOrNull(DEFAULT_WAIT_AFTER_SIM_INSERTED_TIMEOUT_MILLIS) {
+ SubscriptionRepository(mContext!!)
+ .removableSubscriptionInfoListFlow()
+ .firstOrNull { it.isNotEmpty() }
+ }
+
+ if (subscriptionInfos.isNullOrEmpty()) {
+ Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.")
+ return@withContext
+ }
+ Log.d(
+ TAG,
+ "getAvailableRemovableSubscription:$subscriptionInfos"
+ )
+ val psimSubId = subscriptionInfos[0].subscriptionId
+ if (!hasAnyPortActiveInSlot(removableSlotInfo)
+ || hasOtherActiveSubInfo(psimSubId)) {
+ Log.d(TAG, "ForNewUi Start Setup flow")
+ startSimConfirmDialogActivity(psimSubId)
+ }
+ }
+ }
+ }
+
+ private fun handleRemovableSimInsertUnderDsdsMep(removableSlotInfo: UiccSlotInfo) {
+ Log.i(TAG, "Handle Removable SIM inserted under DSDS+Mep.")
+
+ if (removableSlotInfo.ports.stream().findFirst().get().isActive) {
+ Log.i(
+ TAG, "The removable slot is already active. Do nothing. removableSlotInfo: "
+ + removableSlotInfo
+ )
+ return
+ }
+
+ val subscriptionInfos =
+ availableRemovableSubscription
+ if (subscriptionInfos.isEmpty()) {
+ Log.e(TAG, "Unable to find the removable subscriptionInfo. Do nothing.")
+ return
+ }
+ Log.d(
+ TAG,
+ "getAvailableRemovableSubscription:$subscriptionInfos"
+ )
+ startSimConfirmDialogActivity(subscriptionInfos[0].subscriptionId)
+ }
+
+ private fun getLastRemovableSimSlotState(context: Context): Int {
+ val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
+ return prefs.getInt(KEY_REMOVABLE_SLOT_STATE, UiccSlotInfo.CARD_STATE_INFO_ABSENT)
+ }
+
+ private fun setRemovableSimSlotState(context: Context, state: Int) {
+ val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
+ prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply()
+ Log.d(TAG, "setRemovableSimSlotState: $state")
+ }
+
+ private fun getSuwRemovableSlotAction(context: Context): Int {
+ val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
+ return prefs.getInt(KEY_SUW_PSIM_ACTION, LAST_USER_ACTION_IN_SUW_NONE)
+ }
+
+ private fun setSuwRemovableSlotAction(context: Context, action: Int) {
+ val prefs = context.getSharedPreferences(EUICC_PREFS, Context.MODE_PRIVATE)
+ prefs.edit().putInt(KEY_SUW_PSIM_ACTION, action).apply()
+ }
+
+ private val removableUiccSlotInfo: UiccSlotInfo?
+ get() {
+ val slotInfos = mTelMgr!!.uiccSlotsInfo
+ if (slotInfos == null) {
+ Log.e(
+ TAG,
+ "slotInfos is null. Unable to get slot infos."
+ )
+ return null
+ }
+ for (slotInfo in slotInfos) {
+ if (slotInfo != null && slotInfo.isRemovable) {
+ return slotInfo
+ }
+ }
+ return null
+ }
+
+ private fun hasActiveEsimSubscription(): Boolean {
+ val activeSubs = SubscriptionUtil.getActiveSubscriptions(mSubMgr)
+ return activeSubs.stream().anyMatch { subscriptionInfo -> subscriptionInfo.isEmbedded }
+ }
+
+ private val groupedEmbeddedSubscriptions: List<SubscriptionInfo>
+ get() {
+ val groupedSubscriptions =
+ SubscriptionUtil.getSelectableSubscriptionInfoList(mContext)
+ ?: return ImmutableList.of()
+ return ImmutableList.copyOf(
+ groupedSubscriptions.stream()
+ .filter { sub: SubscriptionInfo -> sub.isEmbedded }
+ .collect(Collectors.toList()))
+ }
+
+ protected val availableRemovableSubscription: List<SubscriptionInfo>
+ get() {
+ val 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: SubscriptionInfo -> !sub.isEmbedded }
+ .collect(Collectors.toList()))
+ }
+
+ private fun startChooseSimActivity(psimInserted: Boolean) {
+ val intent = ChooseSimActivity.getIntent(mContext)
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.putExtra(ChooseSimActivity.KEY_HAS_PSIM, psimInserted)
+ mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
+ }
+
+ private fun startSwitchSlotConfirmDialogActivity(subscriptionInfo: SubscriptionInfo) {
+ val intent = Intent(
+ mContext,
+ SwitchToEsimConfirmDialogActivity::class.java
+ )
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.putExtra(SwitchToEsimConfirmDialogActivity.KEY_SUB_TO_ENABLE, subscriptionInfo)
+ mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
+ }
+
+ private fun startDsdsDialogActivity() {
+ val intent = Intent(mContext, DsdsDialogActivity::class.java)
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ mContext!!.startActivityAsUser(intent, UserHandle.SYSTEM)
+ }
+
+ private fun startSimConfirmDialogActivity(subId: Int) {
+ if (!isSuwFinished(mContext!!)) {
+ Log.d(TAG, "Still in SUW. Do nothing")
+ return
+ }
+ if (!SubscriptionManager.isUsableSubscriptionId(subId)) {
+ Log.i(TAG, "Unable to enable subscription due to invalid subscription ID.")
+ return
+ }
+ Log.d(
+ TAG,
+ "Start ToggleSubscriptionDialogActivity with $subId under DSDS+Mep."
+ )
+ SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true, true)
+ }
+
+ private val isMultipleEnabledProfilesSupported: Boolean
+ get() {
+ val cardInfos = mTelMgr!!.uiccCardsInfo
+ if (cardInfos == null) {
+ Log.d(
+ TAG,
+ "UICC cards info list is empty."
+ )
+ return false
+ }
+ return cardInfos.stream()
+ .anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
+ }
+
+ private fun isSuwFinished(context: Context): Boolean {
+ try {
+ // DEVICE_PROVISIONED is 0 if still in setup wizard. 1 if setup completed.
+ return (Settings.Global.getInt(
+ context.contentResolver, Settings.Global.DEVICE_PROVISIONED)
+ == 1)
+ } catch (e: SettingNotFoundException) {
+ Log.e(TAG, "Cannot get DEVICE_PROVISIONED from the device.", e)
+ return false
+ }
+ }
+
+ companion object {
+ private const val TAG = "SimSlotChangeHandler"
+
+ private const val EUICC_PREFS = "euicc_prefs"
+
+ // Shared preference keys
+ private const val KEY_REMOVABLE_SLOT_STATE = "removable_slot_state"
+ private const val KEY_SUW_PSIM_ACTION = "suw_psim_action"
+
+ // User's last removable SIM insertion / removal action during SUW.
+ private const val LAST_USER_ACTION_IN_SUW_NONE = 0
+ private const val LAST_USER_ACTION_IN_SUW_INSERT = 1
+ private const val LAST_USER_ACTION_IN_SUW_REMOVE = 2
+
+ private const val DEFAULT_WAIT_AFTER_SIM_INSERTED_TIMEOUT_MILLIS: Long = 25 * 1000L
+
+ @Volatile
+ private var slotChangeHandler: SimSlotChangeHandler? = null
+
+ /** Returns a SIM slot change handler singleton. */
+ @JvmStatic
+ fun get(): SimSlotChangeHandler? {
+ if (slotChangeHandler == null) {
+ synchronized(SimSlotChangeHandler::class.java) {
+ if (slotChangeHandler == null) {
+ slotChangeHandler = SimSlotChangeHandler()
+ }
+ }
+ }
+ return slotChangeHandler
+ }
+ }
+}
diff --git a/src/com/android/settings/spa/preference/ComposePreference.kt b/src/com/android/settings/spa/preference/ComposePreference.kt
index 91b2d8a..57aa386 100644
--- a/src/com/android/settings/spa/preference/ComposePreference.kt
+++ b/src/com/android/settings/spa/preference/ComposePreference.kt
@@ -28,7 +28,7 @@
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.widget.GroupSectionDividerMixin
-open class ComposeMainSwitchPreference @JvmOverloads constructor(
+open class ComposeGroupSectionPreference @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
diff --git a/src/com/android/settings/wifi/WifiSwitchPreference.kt b/src/com/android/settings/wifi/WifiSwitchPreference.kt
index ba6fb02..9549754 100644
--- a/src/com/android/settings/wifi/WifiSwitchPreference.kt
+++ b/src/com/android/settings/wifi/WifiSwitchPreference.kt
@@ -92,10 +92,15 @@
return true
}
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) =
when {
(value == true && !context.isRadioAllowed()) || isSatelliteOn(context) ->
ReadWritePermit.DISALLOW
diff --git a/src/com/android/settings/wifi/calling/WifiCallingMainSwitchPreference.kt b/src/com/android/settings/wifi/calling/WifiCallingMainSwitchPreference.kt
index f6056f4..07adac0 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingMainSwitchPreference.kt
+++ b/src/com/android/settings/wifi/calling/WifiCallingMainSwitchPreference.kt
@@ -60,10 +60,15 @@
override fun createWidget(context: Context) = SettingsMainSwitchPreference(context)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) =
when {
value == true &&
(DisclaimerItemFactory.create(context, subId).isNotEmpty() ||
diff --git a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
index 8f9741a..4ffe279 100644
--- a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
@@ -57,7 +57,8 @@
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_ADD_DEVICE.equals(preference.getKey())) {
- WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorQrCodeScanner());
+ WifiDppUtils.showLockScreenForWifiSharing(mContext,
+ () -> launchWifiDppConfiguratorQrCodeScanner());
return true; /* click is handled */
}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index a8d7f41..ecddecf 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -980,7 +980,8 @@
* Share the wifi network with QR code.
*/
private void shareNetwork() {
- WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
+ WifiDppUtils.showLockScreenForWifiSharing(mContext,
+ () -> launchWifiDppConfiguratorActivity());
}
/**
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 23a6a54..24ab496 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -16,6 +16,8 @@
package com.android.settings.wifi.dpp;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
@@ -33,6 +35,9 @@
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
@@ -58,6 +63,8 @@
* @see WifiQrCode
*/
public class WifiDppUtils {
+ private static final String TAG = "WifiDppUtils";
+
/**
* The fragment tag specified to FragmentManager for container activities to manage fragments.
*/
@@ -109,7 +116,15 @@
private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
- private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+ /**
+ * Parameters to check whether the device has been locked recently
+ */
+ @VisibleForTesting
+ public static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+ @VisibleForTesting
+ public static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+ @VisibleForTesting
+ public static final int WIFI_SHARING_MAX_UNLOCK_SECONDS = 60;
/**
* Returns whether the device support WiFi DPP.
@@ -426,51 +441,75 @@
* Shows authentication screen to confirm credentials (pin, pattern or password) for the current
* user of the device.
*
- * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
* @param successRunnable The {@code Runnable} which will be executed if the user does not setup
* device security or if lock screen is unlocked
*/
- public static void showLockScreen(Context context, Runnable successRunnable) {
- final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
- Context.KEYGUARD_SERVICE);
-
- if (keyguardManager.isKeyguardSecure()) {
- final BiometricPrompt.AuthenticationCallback authenticationCallback =
- new BiometricPrompt.AuthenticationCallback() {
- @Override
- public void onAuthenticationSucceeded(
- BiometricPrompt.AuthenticationResult result) {
- successRunnable.run();
- }
-
- @Override
- public void onAuthenticationError(int errorCode, CharSequence errString) {
- //Do nothing
- }
- };
-
- final int userId = UserHandle.myUserId();
-
- final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
- .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
-
- if (keyguardManager.isDeviceSecure()) {
- builder.setDeviceCredentialAllowed(true);
- builder.setTextForDeviceCredential(
- null /* title */,
- Utils.getConfirmCredentialStringForUser(
- context, userId, Utils.getCredentialType(context, userId)),
- null /* description */);
- }
-
- final BiometricPrompt bp = builder.build();
- final Handler handler = new Handler(Looper.getMainLooper());
- bp.authenticate(new CancellationSignal(),
- runnable -> handler.post(runnable),
- authenticationCallback);
- } else {
+ public static void showLockScreen(@NonNull Context context, @NonNull Runnable successRunnable) {
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ if (keyguardManager == null || !keyguardManager.isKeyguardSecure()) {
successRunnable.run();
+ return;
}
+ showLockScreen(context, successRunnable, keyguardManager);
+ }
+
+ /**
+ * Shows authentication screen to confirm credentials (pin, pattern or password) for the
+ * current user of the device. But if the device has been unlocked recently, the
+ * authentication screen will be skipped.
+ *
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreenForWifiSharing(@NonNull Context context,
+ @NonNull Runnable successRunnable) {
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ if (keyguardManager == null || !keyguardManager.isKeyguardSecure()) {
+ successRunnable.run();
+ return;
+ }
+ if (isUnlockedWithinSeconds(WIFI_SHARING_KEY_ALIAS, WIFI_SHARING_MAX_UNLOCK_SECONDS)) {
+ Log.d(TAG, "Bypassing the lock screen because the device was unlocked recently.");
+ successRunnable.run();
+ return;
+ }
+ showLockScreen(context, successRunnable, keyguardManager);
+ }
+
+ @SuppressLint("MissingPermission")
+ private static void showLockScreen(@NonNull Context context, @NonNull Runnable successRunnable,
+ @NonNull KeyguardManager keyguardManager) {
+ BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ successRunnable.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ //Do nothing
+ }
+ };
+ int userId = UserHandle.myUserId();
+ BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
+ .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
+ if (keyguardManager.isDeviceSecure()) {
+ builder.setDeviceCredentialAllowed(true);
+ builder.setTextForDeviceCredential(
+ null /* title */,
+ Utils.getConfirmCredentialStringForUser(
+ context, userId, Utils.getCredentialType(context, userId)),
+ null /* description */);
+ }
+ BiometricPrompt bp = builder.build();
+ Handler handler = new Handler(Looper.getMainLooper());
+ bp.authenticate(new CancellationSignal(),
+ runnable -> handler.post(runnable),
+ authenticationCallback);
}
/**
diff --git a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
index 931583a..2f72c4a 100644
--- a/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
+++ b/src/com/android/settings/wifi/tether/WifiHotspotSwitchPreference.kt
@@ -106,11 +106,15 @@
override val restrictionKeys
get() = arrayOf(UserManager.DISALLOW_WIFI_TETHERING)
- override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
+ override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
ReadWritePermit.ALLOW
- override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
- ReadWritePermit.ALLOW
+ override fun getWritePermit(
+ context: Context,
+ value: Boolean?,
+ callingPid: Int,
+ callingUid: Int,
+ ) = ReadWritePermit.ALLOW
override val sensitivityLevel
get() = SensitivityLevel.HIGH_SENSITIVITY
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
index 1bcff1e..d2d26ab 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -123,7 +123,7 @@
}
private void shareHotspotNetwork(Intent intent) {
- WifiDppUtils.showLockScreen(mContext, () -> {
+ WifiDppUtils.showLockScreenForWifiSharing(mContext, () -> {
mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE,
SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
diff --git a/tests/robotests/assets/exempt_not_implementing_index_provider b/tests/robotests/assets/exempt_not_implementing_index_provider
index 7099089..2157491 100644
--- a/tests/robotests/assets/exempt_not_implementing_index_provider
+++ b/tests/robotests/assets/exempt_not_implementing_index_provider
@@ -48,7 +48,6 @@
com.android.settings.fuelgauge.RestrictedAppDetails
com.android.settings.IccLockSettings
com.android.settings.inputmethod.InputMethodAndSubtypeEnabler
-com.android.settings.inputmethod.KeyboardLayoutPickerFragment
com.android.settings.inputmethod.SpellCheckersSettings
com.android.settings.location.LocationPersonalSettings
com.android.settings.location.LocationWorkProfileSettings
diff --git a/tests/robotests/config/robolectric.properties b/tests/robotests/config/robolectric.properties
index 2ec65a0..a879954 100644
--- a/tests/robotests/config/robolectric.properties
+++ b/tests/robotests/config/robolectric.properties
@@ -1,6 +1,7 @@
sdk=NEWEST_SDK
shadows=\
com.android.settings.testutils.shadow.ShadowThreadUtils \
+ com.android.settings.testutils.shadow.ShadowAccessibilityManager \
com.android.settings.network.ShadowServiceManagerExtend
instrumentedPackages=androidx.preference
sqliteMode=native
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
index 0c942e3..9315995 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilitySettingsTest.java
@@ -16,6 +16,8 @@
package com.android.settings.accessibility;
+import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -564,9 +566,8 @@
}
private void setShortcutEnabled(ComponentName componentName, boolean enabled) {
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
- enabled ? componentName.flattenToString() : "");
+ mShadowAccessibilityManager.setAccessibilityShortcutTargets(
+ SOFTWARE, (enabled) ? List.of(componentName.flattenToString()) : List.of());
}
private BooleanSubject assertUriObserversContainsClazz(
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
index 5973d26..c6624b2 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityShortcutPreferenceFragmentTest.java
@@ -19,7 +19,6 @@
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.QUICK_SETTINGS;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
-import static com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import static com.google.common.truth.Truth.assertThat;
@@ -37,11 +36,11 @@
import android.content.Intent;
import android.icu.text.CaseMap;
import android.os.Bundle;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.PopupWindow;
import androidx.annotation.Nullable;
@@ -54,10 +53,10 @@
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
+import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowFragment;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -69,43 +68,31 @@
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowApplication;
+import java.util.List;
import java.util.Locale;
/** Tests for {@link AccessibilityShortcutPreferenceFragment} */
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {
- com.android.settings.testutils.shadow.ShadowFragment.class,
-})
+@Config(shadows = com.android.settings.testutils.shadow.ShadowFragment.class)
public class AccessibilityShortcutPreferenceFragmentTest {
private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example";
private static final String PLACEHOLDER_CLASS_NAME = PLACEHOLDER_PACKAGE_NAME + ".placeholder";
- private static final String PLACEHOLDER_TILE_CLASS_NAME =
- PLACEHOLDER_PACKAGE_NAME + "tile.placeholder";
private static final ComponentName PLACEHOLDER_COMPONENT_NAME = new ComponentName(
PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_CLASS_NAME);
- private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName(
- PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME);
- private static final String PLACEHOLDER_TILE_TOOLTIP_CONTENT =
- PLACEHOLDER_PACKAGE_NAME + "tooltip_content";
- private static final String PLACEHOLDER_DIALOG_TITLE = "title";
-
- private static final String SOFTWARE_SHORTCUT_KEY =
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
- private static final String HARDWARE_SHORTCUT_KEY =
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private TestAccessibilityShortcutPreferenceFragment mFragment;
private PreferenceScreen mScreen;
private Context mContext = ApplicationProvider.getApplicationContext();
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
+ private ShadowAccessibilityManager mShadowAccessibilityManager;
@Before
public void setUpTestFragment() {
MockitoAnnotations.initMocks(this);
+ mShadowAccessibilityManager = Shadow.extract(
+ mContext.getSystemService(AccessibilityManager.class));
mFragment = spy(new TestAccessibilityShortcutPreferenceFragment(null));
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext);
@@ -128,8 +115,10 @@
@Test
public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() {
- putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString());
- putStringIntoSettings(HARDWARE_SHORTCUT_KEY, PLACEHOLDER_COMPONENT_NAME.flattenToString());
+ mShadowAccessibilityManager.setAccessibilityShortcutTargets(
+ SOFTWARE, List.of(PLACEHOLDER_COMPONENT_NAME.flattenToString()));
+ mShadowAccessibilityManager.setAccessibilityShortcutTargets(
+ HARDWARE, List.of(PLACEHOLDER_COMPONENT_NAME.flattenToString()));
mFragment.updateShortcutPreferenceData();
@@ -153,14 +142,6 @@
@Test
@Config(shadows = ShadowFragment.class)
- public void showQuickSettingsTooltipIfNeeded_dontShowTooltipView() {
- mFragment.showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_EDIT);
-
- assertThat(getLatestPopupWindow()).isNull();
- }
-
- @Test
- @Config(shadows = ShadowFragment.class)
public void showGeneralCategory_shouldInitCategory() {
final Bundle savedInstanceState = new Bundle();
when(mFragment.showGeneralCategory()).thenReturn(true);
@@ -261,16 +242,6 @@
}
@Override
- protected ComponentName getTileComponentName() {
- return PLACEHOLDER_TILE_COMPONENT_NAME;
- }
-
- @Override
- protected CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) {
- return PLACEHOLDER_TILE_TOOLTIP_CONTENT;
- }
-
- @Override
public int getUserShortcutTypes() {
return 0;
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java
index bcd7c87..c93ee1e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityUtilTest.java
@@ -45,8 +45,6 @@
import androidx.test.core.app.ApplicationProvider;
-import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
-import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.settings.R;
import org.junit.Before;
@@ -57,7 +55,6 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.util.StringJoiner;
@RunWith(RobolectricTestRunner.class)
public final class AccessibilityUtilTest {
@@ -67,16 +64,6 @@
private static final String MOCK_CLASS_NAME2 = MOCK_PACKAGE_NAME + ".mock_a11y_service2";
private static final ComponentName MOCK_COMPONENT_NAME = new ComponentName(MOCK_PACKAGE_NAME,
MOCK_CLASS_NAME);
- private static final ComponentName MOCK_COMPONENT_NAME2 = new ComponentName(MOCK_PACKAGE_NAME,
- MOCK_CLASS_NAME2);
- private static final String SOFTWARE_SHORTCUT_KEY =
- Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
- private static final String HARDWARE_SHORTCUT_KEY =
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
- private static final String QUICK_SETTINGS_SHORTCUT_KEY =
- Settings.Secure.ACCESSIBILITY_QS_TARGETS;
-
- private static final String PLACEHOLDER_SETTING_FEATURE = "placeholderSettingFeature";
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -162,56 +149,6 @@
}
@Test
- public void hasValueInSettings_putValue_hasValue() {
- setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
-
- assertThat(AccessibilityUtil.hasValueInSettings(mContext, SOFTWARE,
- MOCK_COMPONENT_NAME)).isTrue();
- }
-
- @Test
- public void getUserShortcutTypeFromSettings_putOneValue_hasValue() {
- setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
-
- final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
- MOCK_COMPONENT_NAME);
-
- assertThat(shortcutTypes).isEqualTo(
- SOFTWARE
- );
- }
-
- @Test
- public void getUserShortcutTypeFromSettings_putTwoValues_hasValue() {
- setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
- setShortcut(HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
-
- final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
- MOCK_COMPONENT_NAME);
-
- assertThat(shortcutTypes).isEqualTo(
- SOFTWARE
- | HARDWARE
- );
- }
-
- @Test
- public void getUserShortcutTypeFromSettings_threeShortcutTypesChosen() {
- setShortcut(SOFTWARE, MOCK_COMPONENT_NAME.flattenToString());
- setShortcut(HARDWARE, MOCK_COMPONENT_NAME.flattenToString());
- setShortcut(QUICK_SETTINGS, MOCK_COMPONENT_NAME.flattenToString());
-
- final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
- MOCK_COMPONENT_NAME);
-
- assertThat(shortcutTypes).isEqualTo(
- SOFTWARE
- | HARDWARE
- | QUICK_SETTINGS
- );
- }
-
- @Test
public void convertKeyFromSettings_shortcutTypeSoftware() {
assertThat(AccessibilityUtil.convertKeyFromSettings(SOFTWARE))
.isEqualTo(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS);
@@ -308,28 +245,9 @@
return null;
}
- private String getStringFromSettings(String key) {
- return Settings.Secure.getString(mContext.getContentResolver(), key);
- }
-
private void setSettingsFeatureEnabled(String settingsKey, boolean enabled) {
Settings.Secure.putInt(mContext.getContentResolver(),
settingsKey,
enabled ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF);
}
-
- private void setShortcut(@UserShortcutType int shortcutType, String... componentNames) {
- StringJoiner shortcutComponents = new StringJoiner(":");
- for (String componentName : componentNames) {
- shortcutComponents.add(componentName);
- }
- Settings.Secure.putString(mContext.getContentResolver(),
- ShortcutUtils.convertToKey(shortcutType), shortcutComponents.toString());
- }
-
- private void clearShortcuts() {
- Settings.Secure.putString(mContext.getContentResolver(), SOFTWARE_SHORTCUT_KEY, "");
- Settings.Secure.putString(mContext.getContentResolver(), HARDWARE_SHORTCUT_KEY, "");
- Settings.Secure.putString(mContext.getContentResolver(), QUICK_SETTINGS_SHORTCUT_KEY, "");
- }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java b/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java
new file mode 100644
index 0000000..1b66393
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settings.accessibility;
+
+import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_RESTORED;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_CHANNEL;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_ID;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_SHOWN;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.os.Bundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.graphics.hwui.flags.Flags;
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowNotification;
+import org.robolectric.shadows.ShadowNotificationManager;
+import org.robolectric.shadows.ShadowPackageManager;
+
+/** Tests for {@link HighContrastTextMigrationReceiver}. */
+@RunWith(RobolectricTestRunner.class)
+public class HighContrastTextMigrationReceiverTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private HighContrastTextMigrationReceiver mReceiver;
+ private ShadowNotificationManager mShadowNotificationManager;
+
+ @Before
+ public void setUp() {
+ NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
+ mShadowNotificationManager = Shadows.shadowOf(notificationManager);
+
+ // Setup Settings app as a system app
+ ShadowPackageManager shadowPm = Shadows.shadowOf(mContext.getPackageManager());
+ ComponentName textReadingComponent = new ComponentName(Utils.SETTINGS_PACKAGE_NAME,
+ com.android.settings.Settings.TextReadingSettingsActivity.class.getName());
+ ActivityInfo activityInfo = shadowPm.addActivityIfNotPresent(textReadingComponent);
+ activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ shadowPm.addOrUpdateActivity(activityInfo);
+
+ mReceiver = new HighContrastTextMigrationReceiver();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onReceive_flagOff_settingsNotSet() {
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onRestored_hctStateOn_showPromptHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
+
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, ON);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onRestored_hctStateOff_showPromptHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, OFF);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnknownHctOn_showPromptAndAutoDisableHct() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnknownAndHctOff_promptIsUnnecessaryHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateShownAndHctOn_promptStateUnchangedHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateShownAndHctOff_promptStateUnchangedHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnnecessaryAndHctOn_promptStateUnchangedHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnnecessaryHctOff_promptStateUnchangedHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ private void verifyNotificationNotSent() {
+ Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
+ assertThat(notification).isNull();
+ }
+
+ private void verifyNotificationSent() {
+ // Verify hct channel created
+ assertThat(mShadowNotificationManager.getNotificationChannels().stream().anyMatch(
+ channel -> channel.getId().equals(NOTIFICATION_CHANNEL))).isTrue();
+
+ // Verify hct notification is sent with correct content
+ Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
+ assertThat(notification).isNotNull();
+
+ ShadowNotification shadowNotification = Shadows.shadowOf(notification);
+ assertThat(shadowNotification.getContentTitle()).isEqualTo(mContext.getString(
+ R.string.accessibility_notification_high_contrast_text_title));
+ assertThat(shadowNotification.getContentText()).isEqualTo(
+ mContext.getString(R.string.accessibility_notification_high_contrast_text_content));
+
+ assertThat(notification.actions.length).isEqualTo(1);
+ assertThat(notification.actions[0].title.toString()).isEqualTo(
+ mContext.getString(R.string.accessibility_notification_high_contrast_text_action));
+
+ PendingIntent pendingIntent = notification.actions[0].actionIntent;
+ Intent settingsIntent = Shadows.shadowOf(pendingIntent).getSavedIntent();
+ Bundle fragmentArgs = settingsIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(fragmentArgs).isNotNull();
+ assertThat(fragmentArgs.getString(EXTRA_FRAGMENT_ARG_KEY))
+ .isEqualTo(TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY);
+ }
+
+ private void assertPromptStateAndHctState(
+ @HighContrastTextMigrationReceiver.PromptState int promptState,
+ @AccessibilityUtil.State int hctState) {
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, UNKNOWN))
+ .isEqualTo(promptState);
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF))
+ .isEqualTo(hctState);
+ }
+
+ private void setPromptStateAndHctState(
+ @HighContrastTextMigrationReceiver.PromptState int promptState,
+ @AccessibilityUtil.State int hctState) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, promptState);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, hctState);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 05273fc..ba9eaa5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -29,7 +29,6 @@
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
-import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.PopupWindow;
import android.widget.SeekBar;
@@ -44,7 +43,6 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.testutils.shadow.ShadowFragment;
-import com.android.settings.widget.LabeledSeekBarPreference;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -77,7 +75,7 @@
private Activity mContext;
private PreviewSizeSeekBarController mSeekBarController;
private FontSizeData mFontSizeData;
- private LabeledSeekBarPreference mSeekBarPreference;
+ private AccessibilitySeekBarPreference mSeekBarPreference;
private PreferenceScreen mPreferenceScreen;
private TestFragment mFragment;
@@ -109,7 +107,7 @@
mPreferenceScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
when(mPreferenceScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
- mSeekBarPreference = spy(new LabeledSeekBarPreference(mContext, /* attrs= */ null));
+ mSeekBarPreference = spy(new AccessibilitySeekBarPreference(mContext, /* attrs= */ null));
mSeekBarPreference.setKey(FONT_SIZE_KEY);
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -246,12 +244,11 @@
@Test
@Config(shadows = ShadowFragment.class)
- public void restoreValueFromSavedInstanceState_showTooltipView() {
- final Bundle savedInstanceState = new Bundle();
- savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- mSeekBarController.onCreate(savedInstanceState);
+ public void enabledNeedsQSTooltipReshow_showTooltipView() {
+ mSeekBarPreference.setNeedsQSTooltipReshow(true);
mSeekBarController.displayPreference(mPreferenceScreen);
+ mSeekBarController.onStart();
ShadowLooper.idleMainLooper();
assertThat(getLatestPopupWindow().isShowing()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
index f768e42..375952f 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
@@ -28,7 +28,6 @@
import com.android.settings.display.PreviewPagerAdapter;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
-import com.android.settings.widget.LabeledSeekBarPreference;
import org.junit.Before;
import org.junit.Test;
@@ -54,8 +53,8 @@
private final Context mContext = ApplicationProvider.getApplicationContext();
private TextReadingPreviewController mPreviewController;
private TextReadingPreviewPreference mPreviewPreference;
- private LabeledSeekBarPreference mFontSizePreference;
- private LabeledSeekBarPreference mDisplaySizePreference;
+ private AccessibilitySeekBarPreference mFontSizePreference;
+ private AccessibilitySeekBarPreference mDisplaySizePreference;
@Mock
private DisplaySizeData mDisplaySizeData;
@@ -73,8 +72,8 @@
mPreviewPreference = spy(new TextReadingPreviewPreference(mContext, /* attr= */ null));
mPreviewController = new TextReadingPreviewController(mContext, PREVIEW_KEY, fontSizeData,
mDisplaySizeData);
- mFontSizePreference = new LabeledSeekBarPreference(mContext, /* attr= */ null);
- mDisplaySizePreference = new LabeledSeekBarPreference(mContext, /* attr= */ null);
+ mFontSizePreference = new AccessibilitySeekBarPreference(mContext, /* attr= */ null);
+ mDisplaySizePreference = new AccessibilitySeekBarPreference(mContext, /* attr= */ null);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragmentTest.java
index 390a8ca..50d5ea4 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragmentTest.java
@@ -193,7 +193,6 @@
}
private ToggleDaltonizerPreferenceFragment getFragmentInResumedState() {
-
mActivityController.create().start().resume();
Fragment fragment = mActivityController.get().getSupportFragmentManager().findFragmentById(
R.id.main_content);
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 86322f9..ad5b1b5 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -43,6 +43,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.PopupWindow;
import androidx.fragment.app.FragmentActivity;
@@ -54,6 +55,7 @@
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settings.flags.Flags;
+import com.android.settings.testutils.shadow.ShadowAccessibilityManager;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.widget.TopIntroPreference;
@@ -72,12 +74,14 @@
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowApplication;
+import java.util.List;
import java.util.Locale;
/** Tests for {@link ToggleFeaturePreferenceFragment} */
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowFragment.class,
+ ShadowAccessibilityManager.class
})
public class ToggleFeaturePreferenceFragmentTest {
@Rule
@@ -109,6 +113,7 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceManager mPreferenceManager;
+ private ShadowAccessibilityManager mShadowAccessibilityManager;
@Mock
private FragmentActivity mActivity;
@@ -120,6 +125,8 @@
@Before
public void setUpTestFragment() {
MockitoAnnotations.initMocks(this);
+ mShadowAccessibilityManager = Shadow.extract(
+ mContext.getSystemService(AccessibilityManager.class));
mFragment = spy(new TestToggleFeaturePreferenceFragment());
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
@@ -178,10 +185,10 @@
@Test
public void updateShortcutPreferenceData_hasValueInSettings_assignToVariable() {
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
- putSecureStringIntoSettings(SOFTWARE_SHORTCUT_KEY,
- PLACEHOLDER_COMPONENT_NAME.flattenToString());
- putSecureStringIntoSettings(HARDWARE_SHORTCUT_KEY,
- PLACEHOLDER_COMPONENT_NAME.flattenToString());
+ mShadowAccessibilityManager.setAccessibilityShortcutTargets(
+ SOFTWARE, List.of(PLACEHOLDER_COMPONENT_NAME.flattenToString()));
+ mShadowAccessibilityManager.setAccessibilityShortcutTargets(
+ HARDWARE, List.of(PLACEHOLDER_COMPONENT_NAME.flattenToString()));
mFragment.updateShortcutPreferenceData();
@@ -316,14 +323,6 @@
}
@Test
- @Config(shadows = ShadowFragment.class)
- public void showQuickSettingsTooltipIfNeeded_dontShowTooltipView() {
- mFragment.showQuickSettingsTooltipIfNeeded(QuickSettingsTooltipType.GUIDE_TO_EDIT);
-
- assertThat(getLatestPopupWindow()).isNull();
- }
-
- @Test
public void getShortcutTypeSummary_shortcutSummaryIsCorrectlySet() {
final PreferredShortcut userPreferredShortcut = new PreferredShortcut(
PLACEHOLDER_COMPONENT_NAME.flattenToString(),
@@ -347,10 +346,6 @@
assertThat(summary).isEqualTo(expected);
}
- private void putSecureStringIntoSettings(String key, String componentName) {
- Settings.Secure.putString(mContext.getContentResolver(), key, componentName);
- }
-
private String getSecureStringFromSettings(String key) {
return Settings.Secure.getString(mContext.getContentResolver(), key);
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index 9609af4..2251c3b 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -124,24 +124,17 @@
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(ImmutableSet.of());
- mBluetoothDeviceUpdater =
- spy(
- new AvailableMediaBluetoothDeviceUpdater(
- mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0));
- mBluetoothDeviceUpdater.setPrefContext(mContext);
mPreference =
new BluetoothDevicePreference(
mContext,
mCachedBluetoothDevice,
false,
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
- doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
- doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
}
@Test
public void onAudioModeChanged_hfpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -153,7 +146,7 @@
@Test
public void onAudioModeChanged_hfpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -165,7 +158,7 @@
@Test
public void onAudioModeChanged_a2dpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -177,7 +170,7 @@
@Test
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -189,7 +182,7 @@
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -202,7 +195,7 @@
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -215,7 +208,7 @@
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -228,7 +221,7 @@
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -241,7 +234,7 @@
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -256,7 +249,7 @@
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -272,7 +265,7 @@
@Test
public void onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -292,7 +285,7 @@
@Test
public void onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -309,7 +302,7 @@
@Test
public void onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -326,7 +319,7 @@
@Test
public void onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -344,7 +337,7 @@
public void onProfileConnectionStateChanged_leaConnected_notInCallInSharing_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -367,7 +360,7 @@
onProfileConnectionStateChanged_leaConnected_noInCallInSharing_hysteresis_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -388,7 +381,7 @@
public void onProfileConnectionStateChanged_leaConnected_inCallSharing_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -410,7 +403,7 @@
public void onProfileConnectionStateChanged_leaConnected_inCallSharing_hysteresis_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -430,7 +423,7 @@
@Test
public void
onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -446,7 +439,7 @@
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -462,6 +455,7 @@
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
@@ -470,8 +464,19 @@
@Test
public void onClick_Preference_setActive() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
verify(mDevicePreferenceCallback).onDeviceClick(mPreference);
}
+
+ private void setUpDeviceUpdaterWithAudioMode(int audioMode) {
+ mAudioManager.setMode(audioMode);
+ mBluetoothDeviceUpdater =
+ spy(new AvailableMediaBluetoothDeviceUpdater(
+ mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0));
+ mBluetoothDeviceUpdater.setPrefContext(mContext);
+ doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
+ doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index b2449da..f68a8d4 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -112,16 +112,11 @@
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
- mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
- mDevicePreferenceCallback, /* metricsCategory= */ 0));
- mBluetoothDeviceUpdater.setPrefContext(mContext);
- doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
- doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
}
@Test
public void onAudioModeChanged_hfpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -133,7 +128,7 @@
@Test
public void onAudioModeChanged_hfpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -145,7 +140,7 @@
@Test
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -157,7 +152,7 @@
@Test
public void onAudioModeChanged_a2dpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -169,7 +164,7 @@
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -182,7 +177,7 @@
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -196,7 +191,7 @@
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -209,7 +204,7 @@
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -222,7 +217,7 @@
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -236,7 +231,7 @@
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_removePreference()
{
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -250,7 +245,7 @@
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_removePreference()
{
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -263,7 +258,7 @@
@Test
public void onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_removesPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -277,7 +272,7 @@
@Test
public void onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_removesPreference()
{
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -290,7 +285,7 @@
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()
{
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -305,7 +300,7 @@
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference
() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -319,6 +314,7 @@
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
@@ -327,6 +323,7 @@
@Test
public void addPreference_addPreference_shouldHideSecondTarget() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
BluetoothDevicePreference btPreference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
true, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
@@ -340,7 +337,7 @@
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_notExclusiveManagedDevice_addDevice() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -356,7 +353,7 @@
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
throws Exception {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -376,7 +373,7 @@
throws Exception {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.enabled = false;
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -393,7 +390,7 @@
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()
throws Exception {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -407,4 +404,13 @@
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
}
+
+ private void setUpDeviceUpdaterWithAudioMode(int audioMode) {
+ mAudioManager.setMode(audioMode);
+ mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
+ mDevicePreferenceCallback, /* metricsCategory= */ 0));
+ mBluetoothDeviceUpdater.setPrefContext(mContext);
+ doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
+ doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
index 4e962c7..6aff8c3 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
@@ -54,7 +54,7 @@
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
- ShadowBluetoothAdapter.class,
+ ShadowBluetoothAdapter.class,
})
public class AudioStreamsProgressCategoryCallbackTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -70,8 +70,8 @@
@Before
public void setUp() {
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
- BluetoothAdapter.getDefaultAdapter());
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
@@ -87,7 +87,7 @@
when(mState.getBisSyncState()).thenReturn(bisSyncState);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
- verify(mController).handleSourceConnected(any());
+ verify(mController).handleSourceConnected(any(), any());
}
@Test
@@ -102,7 +102,7 @@
when(mSourceDevice.getAddress()).thenReturn(address);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
- verify(mController).handleSourcePresent(any());
+ verify(mController).handleSourcePresent(any(), any());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
index 78d4d6e..f042329 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -41,7 +42,7 @@
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
-import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -93,6 +94,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -134,8 +136,8 @@
@Before
public void setUp() {
- ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
- BluetoothAdapter.getDefaultAdapter());
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
@@ -143,7 +145,7 @@
BluetoothStatusCodes.FEATURE_SUPPORTED);
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
@@ -310,14 +312,12 @@
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
- List<BluetoothLeBroadcastReceiveState> connectedList = new ArrayList<>();
// Empty connected device list
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(connectedList);
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
- verify(mAudioStreamsHelper).getAllPresentSources();
verify(mLeBroadcastAssistant).startSearchingForSources(any());
var dialog = ShadowAlertDialog.getLatestAlertDialog();
@@ -355,7 +355,7 @@
}
@Test
- public void testOnStart_handleSourceAlreadyConnected() {
+ public void testOnStart_handleSourceAlreadyConnected_useNameFromMetadata() {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
@@ -363,8 +363,14 @@
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
List<BluetoothLeBroadcastReceiveState> list = new ArrayList<>();
+ var data = mock(BluetoothLeAudioContentMetadata.class);
+ when(connected.getSubgroupMetadata()).thenReturn(ImmutableList.of(data));
+ when(data.getProgramInfo()).thenReturn(BROADCAST_NAME_1);
list.add(connected);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(list);
+ when(mMetadata.getBroadcastId()).thenReturn(ALREADY_CONNECTED_BROADCAST_ID);
+ when(mMetadata.getBroadcastName()).thenReturn(BROADCAST_NAME_2);
+ when(mLeBroadcastAssistant.getSourceMetadata(any(), anyInt())).thenReturn(mMetadata);
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(Map.of(mSourceDevice, list));
// Handle already connected source in onStart
mController.displayPreference(mScreen);
@@ -382,6 +388,7 @@
assertThat(preference.getValue()).isNotNull();
assertThat(preference.getValue().getAudioStreamBroadcastId())
.isEqualTo(ALREADY_CONNECTED_BROADCAST_ID);
+ assertThat(preference.getValue().getTitle()).isEqualTo(BROADCAST_NAME_2);
assertThat(state.getValue()).isEqualTo(SOURCE_ADDED);
}
@@ -409,7 +416,8 @@
var data = mock(BluetoothLeAudioContentMetadata.class);
when(connected.getSubgroupMetadata()).thenReturn(ImmutableList.of(data));
when(data.getProgramInfo()).thenReturn(BROADCAST_NAME_1);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, ImmutableList.of(connected)));
// Handle both source from qr code and already connected source in onStart
mController.displayPreference(mScreen);
@@ -578,8 +586,8 @@
// Setup source already connected
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
-
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle source already connected in onStart
mController.displayPreference(mScreen);
mController.onStart(mLifecycleOwner);
@@ -687,7 +695,8 @@
// Setup already connected source
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle connected source in onStart
mController.displayPreference(mScreen);
@@ -695,7 +704,7 @@
shadowOf(Looper.getMainLooper()).idle();
// The connect source is no longer connected
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mController.handleSourceRemoved();
shadowOf(Looper.getMainLooper()).idle();
@@ -728,7 +737,8 @@
// Setup a connected source
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle connected source in onStart
mController.displayPreference(mScreen);
@@ -834,7 +844,7 @@
when(receiveState.getBisSyncState()).thenReturn(bisSyncState);
// The new found source is identified as failed to connect
- mController.handleSourcePresent(receiveState);
+ mController.handleSourcePresent(mSourceDevice, receiveState);
shadowOf(Looper.getMainLooper()).idle();
ArgumentCaptor<AudioStreamPreference> preference =
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
index c7d0c60..e5e51fc 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -31,6 +32,7 @@
import org.robolectric.annotation.Resetter;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
@Implements(value = AudioStreamsHelper.class, callThroughByDefault = true)
@@ -60,6 +62,11 @@
}
@Implementation
+ public Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getAllSourcesByDevice() {
+ return sMockHelper.getAllSourcesByDevice();
+ }
+
+ @Implementation
public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
return sMockHelper.getAllPresentSources();
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/display/DisplayTopologyPreferenceTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/display/DisplayTopologyPreferenceTest.kt
new file mode 100644
index 0000000..ad633cc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/connecteddevice/display/DisplayTopologyPreferenceTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 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.connecteddevice.display
+
+import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.ColorDrawable
+import android.hardware.display.DisplayTopology
+import android.view.View
+import android.widget.FrameLayout
+import androidx.preference.PreferenceViewHolder
+import androidx.test.core.app.ApplicationProvider
+
+import com.android.settings.R
+import com.google.common.truth.Truth.assertThat
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class DisplayTopologyPreferenceTest {
+ val context = ApplicationProvider.getApplicationContext<Context>()
+ val preference = DisplayTopologyPreference(context)
+ val injector = TestInjector()
+ val rootView = View.inflate(context, preference.layoutResource, /*parent=*/ null)
+ val holder = PreferenceViewHolder.createInstanceForTests(rootView)
+ val wallpaper = ColorDrawable(Color.MAGENTA)
+
+ init {
+ preference.injector = injector
+ injector.systemWallpaper = wallpaper
+ preference.onBindViewHolder(holder)
+ }
+
+ class TestInjector : DisplayTopologyPreference.Injector() {
+ var topology : DisplayTopology? = null
+ var systemWallpaper : Drawable? = null
+
+ override fun displayTopology(context : Context) : DisplayTopology? { return topology }
+
+ override fun wallpaper(context : Context) : Drawable { return systemWallpaper!! }
+ }
+
+ @Test
+ fun disabledTopology() {
+ preference.onAttached()
+ preference.onGlobalLayout()
+
+ assertThat(preference.mPaneContent.childCount).isEqualTo(0)
+ // TODO(b/352648432): update test when we show the main display even when
+ // a topology is not active.
+ assertThat(preference.mTopologyHint.text).isEqualTo("")
+ }
+
+ @Test
+ fun twoDisplaysGenerateBlocks() {
+ val child = DisplayTopology.TreeNode(
+ /* displayId= */ 42, /* width= */ 100f, /* height= */ 80f,
+ POSITION_LEFT, /* offset= */ 42f)
+ val root = DisplayTopology.TreeNode(
+ /* displayId= */ 0, /* width= */ 200f, /* height= */ 160f,
+ POSITION_LEFT, /* offset= */ 0f)
+ root.addChild(child)
+ injector.topology = DisplayTopology(root, /*primaryDisplayId=*/ 0)
+
+ // This layoutParams needs to be non-null for the global layout handler.
+ preference.mPaneHolder.layoutParams = FrameLayout.LayoutParams(
+ /* width= */ 640, /* height= */ 480)
+
+ // Force pane width to have a reasonable value (hundreds of dp) so the TopologyScale is
+ // calculated reasonably.
+ preference.mPaneContent.left = 0
+ preference.mPaneContent.right = 640
+
+ preference.onAttached()
+ preference.onGlobalLayout()
+
+ assertThat(preference.mPaneContent.childCount).isEqualTo(2)
+ val block0 = preference.mPaneContent.getChildAt(0)
+ val block1 = preference.mPaneContent.getChildAt(1)
+
+ // Block of child display is on the left.
+ val (childBlock, rootBlock) = if (block0.x < block1.x)
+ listOf(block0, block1)
+ else
+ listOf(block1, block0)
+
+ // After accounting for padding, child should be half the length of root in each dimension.
+ assertThat(childBlock.layoutParams.width + BLOCK_PADDING)
+ .isEqualTo(rootBlock.layoutParams.width / 2)
+ assertThat(childBlock.layoutParams.height + BLOCK_PADDING)
+ .isEqualTo(rootBlock.layoutParams.height / 2)
+ assertThat(childBlock.y).isGreaterThan(rootBlock.y)
+ assertThat(block0.background).isEqualTo(wallpaper)
+ assertThat(block1.background).isEqualTo(wallpaper)
+ assertThat(rootBlock.x - BLOCK_PADDING * 2)
+ .isEqualTo(childBlock.x + childBlock.layoutParams.width)
+
+ assertThat(preference.mTopologyHint.text)
+ .isEqualTo(context.getString(R.string.external_display_topology_hint))
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/display/TopologyScaleTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/display/TopologyScaleTest.kt
index 0784362..76f9bfc 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/display/TopologyScaleTest.kt
+++ b/tests/robotests/src/com/android/settings/connecteddevice/display/TopologyScaleTest.kt
@@ -35,6 +35,7 @@
fun oneDisplay4to3Aspect() {
val scale = TopologyScale(
/* paneWidth= */ 640,
+ minEdgeLength = 48, maxBlockRatio = 0.05f,
listOf(RectF(0f, 0f, 640f, 480f)))
// blockRatio is higher than 0.05 in order to make the smallest display edge (480 dp) 48dp
@@ -51,6 +52,7 @@
fun twoUnalignedDisplays() {
val scale = TopologyScale(
/* paneWidth= */ 300,
+ minEdgeLength = 48, maxBlockRatio = 0.05f,
listOf(RectF(0f, 0f, 1920f, 1200f), RectF(1920f, -300f, 3840f, 900f)))
assertEquals(
@@ -65,6 +67,7 @@
fun twoDisplaysBlockRatioBumpedForGarSizeMinimumHorizontal() {
val scale = TopologyScale(
/* paneWidth= */ 192,
+ minEdgeLength = 48, maxBlockRatio = 0.05f,
listOf(RectF(0f, 0f, 240f, 320f), RectF(-240f, -320f, 0f, 0f)))
// blockRatio is higher than 0.05 in order to make the smallest display edge (240 dp) 48dp
@@ -81,6 +84,7 @@
fun paneVerticalPaddingLimitedByTallestDisplay() {
val scale = TopologyScale(
/* paneWidth= */ 300,
+ minEdgeLength = 48, maxBlockRatio = 0.05f,
listOf(
RectF(0f, 0f, 640f, 480f),
RectF(0f, 480f, 640f, 960f),
@@ -94,4 +98,35 @@
assertEquals(Point(150, 48), scale.displayToPaneCoor(PointF(320f, 0f)))
assertPointF(-180f, 2880f, 0.001f, scale.paneToDisplayCoor(Point(100, 336)))
}
+
+ @Test
+ fun limitedByCustomMaxBlockRatio() {
+ val scale = TopologyScale(
+ /* paneWidth= */ 300,
+ minEdgeLength = 24, maxBlockRatio = 0.12f,
+ listOf(
+ RectF(0f, 0f, 640f, 480f),
+ RectF(0f, 480f, 640f, 960f)))
+
+ assertEquals(
+ "{TopoScale blockRatio=0.120000 originPaneXY=111,57 paneHeight=230}", "" + scale)
+ assertEquals(Point(149, 57), scale.displayToPaneCoor(PointF(320f, 0f)))
+ assertPointF(-91.6667f, 2325f, 0.001f, scale.paneToDisplayCoor(Point(100, 336)))
+ }
+
+ @Test
+ fun largeCustomMinEdgeLength() {
+ // minBlockEdgeLength/minDisplayEdgeLength = 80/480 = 1/6, so the block ratio will be 1/6
+ val scale = TopologyScale(
+ /* paneWidth= */ 300,
+ minEdgeLength = 80, maxBlockRatio = 0.05f,
+ listOf(
+ RectF(0f, 0f, 640f, 480f),
+ RectF(0f, 480f, 640f, 960f)))
+
+ assertEquals(
+ "{TopoScale blockRatio=0.166667 originPaneXY=96,80 paneHeight=320}", "" + scale)
+ assertEquals(Point(149, 80), scale.displayToPaneCoor(PointF(320f, 0f)))
+ assertPointF(24f, 1536f, 0.001f, scale.paneToDisplayCoor(Point(100, 336)))
+ }
}
diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
index 4b8c9de..ce33401 100644
--- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
@@ -28,11 +28,13 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -42,6 +44,7 @@
import android.os.Process;
import android.telephony.SubscriptionManager;
import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceManager;
@@ -51,22 +54,20 @@
import com.android.settings.applications.AppInfoBase;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowDataUsageUtils;
-import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal;
-import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.net.UidDetail;
import com.android.settingslib.net.UidDetailProvider;
+import com.android.settingslib.widget.IntroPreference;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Answers;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
@@ -79,27 +80,25 @@
import java.util.List;
@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {ShadowEntityHeaderController.class, ShadowRestrictedLockUtilsInternal.class})
+@Config(shadows = {ShadowRestrictedLockUtilsInternal.class})
public class AppDataUsageTest {
- @Mock(answer = Answers.RETURNS_DEEP_STUBS)
- private EntityHeaderController mHeaderController;
@Mock
private PackageManager mPackageManager;
+ private IntroPreference mIntroPreference;
+
private AppDataUsage mFragment;
+ private Context mContext;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- ShadowEntityHeaderController.setUseMock(mHeaderController);
- when(mHeaderController.setUid(anyInt())).thenReturn(mHeaderController);
- }
-
- @After
- public void tearDown() {
- ShadowEntityHeaderController.reset();
+ mContext = spy(RuntimeEnvironment.application);
+ mIntroPreference = new IntroPreference(mContext);
+ FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_ENABLE_SPA, true);
}
@Test
@@ -161,6 +160,7 @@
}
@Test
+ @Config(shadows = ShadowFragment.class)
public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
mFragment = spy(new TestFragment());
@@ -169,12 +169,20 @@
doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
ReflectionHelpers.setField(mFragment, "mAppItem", mock(AppItem.class));
- mFragment.addEntityHeader();
+ when(mFragment.getPreferenceScreen().findPreference(AppDataUsage.ARG_APP_HEADER))
+ .thenReturn(mIntroPreference);
+ when(mFragment.getContext()).thenReturn(mContext);
+ doNothing().when(mContext).startActivity(any());
- verify(mHeaderController).setHasAppInfoLink(false);
+ mFragment.setupIntroPreference();
+ mFragment.onPreferenceTreeClick(mIntroPreference);
+
+ verify(mFragment, never()).getActivity();
+ verify(mContext, never()).startActivity(any(Intent.class));
}
@Test
+ @Config(shadows = ShadowFragment.class)
public void bindAppHeader_workApp_shouldSetWorkAppUid()
throws PackageManager.NameNotFoundException {
final int fakeUserId = 100;
@@ -188,19 +196,21 @@
ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
ReflectionHelpers.setField(mFragment, "mPackages", packages);
- when(mPackageManager.getPackageUidAsUser(anyString(), anyInt()))
- .thenReturn(fakeUserId);
-
- when(mHeaderController.setHasAppInfoLink(anyBoolean())).thenReturn(mHeaderController);
-
+ when(mPackageManager.getPackageUidAsUser(anyString(), anyInt())).thenReturn(fakeUserId);
when(mFragment.getPreferenceManager())
.thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
- mFragment.addEntityHeader();
+ when(mFragment.getPreferenceScreen().findPreference(AppDataUsage.ARG_APP_HEADER))
+ .thenReturn(mIntroPreference);
+ when(mFragment.getContext()).thenReturn(mContext);
+ doNothing().when(mContext).startActivity(any());
- verify(mHeaderController).setHasAppInfoLink(true);
- verify(mHeaderController).setUid(fakeUserId);
+ mFragment.setupIntroPreference();
+ mFragment.onPreferenceTreeClick(mIntroPreference);
+
+ ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivity(argumentCaptor.capture());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java
deleted file mode 100644
index f9b566e..0000000
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimeFormatPreferenceControllerTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.datetime;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Context;
-import android.content.Intent;
-import android.provider.Settings;
-import android.text.format.DateFormat;
-
-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;
-import org.robolectric.shadows.ShadowApplication;
-
-import java.util.List;
-import java.util.Locale;
-
-@RunWith(RobolectricTestRunner.class)
-public class AutoTimeFormatPreferenceControllerTest {
-
- @Mock
- private UpdateTimeAndDateCallback mCallback;
-
- private ShadowApplication mApplication;
- private Context mContext;
- private SwitchPreference mPreference;
- private TestAutoTimeFormatPreferenceController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mApplication = ShadowApplication.getInstance();
- mContext = RuntimeEnvironment.application;
- mController = new TestAutoTimeFormatPreferenceController(mContext, "test_key");
- mPreference = new SwitchPreference(mContext);
- mPreference.setKey("test_key");
- }
-
- @Test
- public void updateState_24HourSet_shouldCheckPreference() {
- Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24,
- TimeFormatPreferenceController.HOURS_24);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isFalse();
- }
-
- @Test
- public void updateState_12HourSet_shouldCheckPreference() {
- Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24,
- TimeFormatPreferenceController.HOURS_12);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isFalse();
- }
-
- @Test
- public void updateState_autoSet_shouldNotCheckPreference() {
- Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isChecked()).isTrue();
- }
-
- @Test
- public void updatePreference_autoSet_shouldSendIntent_12HourLocale() {
- mController.setChecked(false);
-
- List<Intent> intentsFired = mApplication.getBroadcastIntents();
- assertThat(intentsFired.size()).isEqualTo(1);
- Intent intentFired = intentsFired.get(0);
- assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED);
- assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1))
- .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR);
- }
-
- @Test
- public void updatePreference_autoSet_shouldSendIntent_24HourLocale() {
- mController.setIs24HourLocale(true);
-
- mController.setChecked(false);
-
- List<Intent> intentsFired = mApplication.getBroadcastIntents();
- assertThat(intentsFired.size()).isEqualTo(1);
- Intent intentFired = intentsFired.get(0);
- assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED);
- assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1))
- .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR);
- }
-
- @Test
- public void updatePreference_24HourSet_shouldSendIntent() {
- mController.setIs24HourLocale(false);
-
- mController.setChecked(true);
-
- List<Intent> intentsFired = mApplication.getBroadcastIntents();
- assertThat(intentsFired.size()).isEqualTo(1);
- Intent intentFired = intentsFired.get(0);
- assertThat(intentFired.getAction()).isEqualTo(Intent.ACTION_TIME_CHANGED);
- assertThat(intentFired.getIntExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, -1))
- .isEqualTo(Intent.EXTRA_TIME_PREF_VALUE_USE_LOCALE_DEFAULT);
- }
-
- /**
- * Extend class under test to change {@link #is24HourLocale} to not call
- * {@link DateFormat#is24HourLocale(Locale)} because that's not available in roboelectric.
- */
- private static class TestAutoTimeFormatPreferenceController
- extends AutoTimeFormatPreferenceController {
-
- private boolean is24HourLocale = false;
-
- TestAutoTimeFormatPreferenceController(Context context, String preferenceKey) {
- super(context, preferenceKey);
- }
-
- void setIs24HourLocale(boolean value) {
- is24HourLocale = value;
- }
-
- @Override
- boolean is24HourLocale(Locale locale) {
- return is24HourLocale;
- }
- }
-}
diff --git a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
index 7bf8d52..2961935 100644
--- a/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/AutoTimeZonePreferenceControllerTest.java
@@ -40,17 +40,12 @@
import android.app.time.TimeZoneDetectorStatus;
import android.content.Context;
import android.os.UserHandle;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.Preference;
import com.android.settings.R;
-import com.android.settings.flags.Flags;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -62,9 +57,6 @@
@RunWith(RobolectricTestRunner.class)
public class AutoTimeZonePreferenceControllerTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
@Mock
private UpdateTimeAndDateCallback mCallback;
private Context mContext;
@@ -247,7 +239,6 @@
}
@Test
- @EnableFlags({Flags.FLAG_REVAMP_TOGGLES})
public void toggleOff_revampFlagOn_shouldToggleOffUseLocation() {
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(
/* autoSupported= */ true,
@@ -266,25 +257,6 @@
verify(mTimeManager).updateTimeZoneConfiguration(configuration);
}
- @Test
- @DisableFlags({Flags.FLAG_REVAMP_TOGGLES})
- public void toggleOff_revampFlagOff_shouldToggleOffUseLocation() {
- TimeZoneCapabilitiesAndConfig capabilitiesAndConfig = createCapabilitiesAndConfig(
- /* autoSupported= */ true,
- /* autoEnabled= */ true,
- /* telephonySupported= */ true,
- /* locationSupported= */ true);
- when(mTimeManager.getTimeZoneCapabilitiesAndConfig()).thenReturn(capabilitiesAndConfig);
-
- mController.setChecked(false);
-
- TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder()
- .setAutoDetectionEnabled(false)
- .build();
-
- verify(mTimeManager).updateTimeZoneConfiguration(configuration);
- }
-
private static TimeZoneCapabilitiesAndConfig createCapabilitiesAndConfig(
boolean autoSupported, boolean autoEnabled, boolean telephonySupported) {
return createCapabilitiesAndConfig(autoSupported, autoEnabled, telephonySupported, false);
diff --git a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
index 40794d2..2f23257 100644
--- a/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/LocationTimeZoneDetectionPreferenceControllerTest.java
@@ -45,17 +45,13 @@
import android.app.time.TimeZoneDetectorStatus;
import android.content.Context;
import android.os.UserHandle;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.core.InstrumentedPreferenceFragment;
-import com.android.settings.flags.Flags;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -71,9 +67,6 @@
})
public class LocationTimeZoneDetectionPreferenceControllerTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
@Mock
private TimeManager mTimeManager;
private Context mContext;
@@ -131,8 +124,7 @@
}
@Test
- @EnableFlags({Flags.FLAG_REVAMP_TOGGLES})
- public void flagRevampTogglesOn_toggleOff_automaticTimeZone_disablesLocationToggle() {
+ public void toggleOff_automaticTimeZone_disablesLocationToggle() {
TimeZoneCapabilitiesAndConfig capabilitiesAndConfig =
createTimeZoneCapabilitiesAndConfig(/* useLocationEnabled= */ true,
CAPABILITY_POSSESSED, /* setAutoDetectionEnabled= */ false);
diff --git a/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java
index c5aac84..a341922 100644
--- a/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/datetime/TimeFormatPreferenceControllerTest.java
@@ -23,16 +23,11 @@
import android.content.Context;
import android.content.Intent;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.preference.SwitchPreference;
-import com.android.settings.flags.Flags;
-
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -46,9 +41,6 @@
@RunWith(RobolectricTestRunner.class)
public class TimeFormatPreferenceControllerTest {
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
@Mock
private UpdateTimeAndDateCallback mCallback;
@@ -105,16 +97,6 @@
}
@Test
- @DisableFlags({Flags.FLAG_REVAMP_TOGGLES})
- public void updateState_autoSet_shouldNotEnablePreference() {
- Settings.System.putString(mContext.getContentResolver(), Settings.System.TIME_12_24, null);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isEnabled()).isFalse();
- }
-
- @Test
public void updatePreference_12HourSet_shouldSendIntent() {
mController.setChecked(false);
diff --git a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizardTest.java b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizardTest.java
index 1113d68..8f0b4a5 100644
--- a/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/display/AutoBrightnessPreferenceControllerForSetupWizardTest.java
@@ -16,19 +16,28 @@
package com.android.settings.display;
+
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
import com.android.settings.accessibility.Flags;
import com.android.settings.testutils.shadow.SettingsShadowResources;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
import org.junit.Rule;
@@ -58,35 +67,79 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+
mController =
new AutoBrightnessPreferenceControllerForSetupWizard(mContext, PREFERENCE_KEY);
}
@Test
@EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
- public void getAvailabilityStatus_configTrueAndFlagOn_shouldReturnAvailableUnsearchable() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_automatic_brightness_available, true);
+ public void displayPreference_flagOn_preferenceVisibleTrue() {
+ Preference preference =
+ displayPreference(/* configAvailable= */ true, /* restricted= */ false);
+ assertThat(preference.isVisible()).isTrue();
+ }
+ @Test
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void displayPreference_flagOnAndRestricted_preferenceVisibleFalse() {
+ Preference preference =
+ displayPreference(/* configAvailable= */ true, /* restricted= */ true);
+ assertThat(preference.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_configTrueAndFlagOn_availableUnsearchable() {
+ displayPreference(/* configAvailable= */ true, /* restricted= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
@Test
@EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
- public void getAvailabilityStatus_configFalseSetAndFlagOn_shouldReturnUnsupportedOnDevice() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_automatic_brightness_available, false);
+ public void getAvailabilityStatus_configTrueAndFlagOnAndRestricted_conditionallyUnavailable() {
+ displayPreference(/* configAvailable= */ true, /* restricted= */ true);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+ @Test
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_configFalseAndFlagOn_unsupportedOnDevice() {
+ displayPreference(/* configAvailable= */ false, /* restricted= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
- @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
- public void getAvailabilityStatus_flagOff_shouldReturnConditionallyUnavailable() {
- SettingsShadowResources.overrideResource(
- com.android.internal.R.bool.config_automatic_brightness_available, true);
-
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_configFalseAndFlagOnAndRestricted_conditionallyUnavailable() {
+ displayPreference(/* configAvailable= */ false, /* restricted= */ true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_flagOff_conditionallyUnavailable() {
+ displayPreference(/* configAvailable= */ true, /* restricted= */ false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ private RestrictedSwitchPreference displayPreference(
+ boolean configAvailable, boolean restricted) {
+ SettingsShadowResources.overrideResource(
+ com.android.internal.R.bool.config_automatic_brightness_available, configAvailable);
+
+ final PreferenceManager manager = new PreferenceManager(mContext);
+ final PreferenceScreen screen = manager.createPreferenceScreen(mContext);
+ final RestrictedSwitchPreference preference = new RestrictedSwitchPreference(mContext);
+ preference.setKey(mController.getPreferenceKey());
+ preference.setDisabledByAdmin(restricted
+ ? mock(RestrictedLockUtils.EnforcedAdmin.class)
+ : null);
+ assertThat(preference.isDisabledByAdmin()).isEqualTo(restricted);
+ screen.addPreference(preference);
+
+ mController.displayPreference(screen);
+ return preference;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizardTest.java b/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizardTest.java
index 25ddd1e..6e44464 100644
--- a/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizardTest.java
+++ b/tests/robotests/src/com/android/settings/display/BrightnessLevelPreferenceControllerForSetupWizardTest.java
@@ -21,12 +21,20 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
import com.android.settings.accessibility.Flags;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedPreference;
import org.junit.Before;
import org.junit.Rule;
@@ -51,19 +59,58 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+
mController = new BrightnessLevelPreferenceControllerForSetupWizard(mContext,
/* lifecycle= */ null);
}
@Test
@EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
- public void getAvailabilityStatus_flagOn_shouldReturnAvailable() {
+ public void displayPreference_flagOn_preferenceVisibleTrue() {
+ Preference preference = displayPreference(/* restricted= */ false);
+ assertThat(preference.isVisible()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void displayPreference_flagOnAndRestricted_preferenceVisibleFalse() {
+ Preference preference = displayPreference(/* restricted= */ true);
+ assertThat(preference.isVisible()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_flagOn_available() {
+ displayPreference(/* restricted= */ false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
- @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
- public void getAvailabilityStatus_flagOff_shouldReturnConditionallyUnavailable() {
+ @EnableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_flagOnAndRestricted_conditionallyUnavailable() {
+ displayPreference(/* restricted= */ true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_ADD_BRIGHTNESS_SETTINGS_IN_SUW)
+ public void getAvailabilityStatus_flagOff_conditionallyUnavailable() {
+ displayPreference(/* restricted= */ false);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
+ }
+
+ private RestrictedPreference displayPreference(boolean restricted) {
+ final PreferenceManager manager = new PreferenceManager(mContext);
+ final PreferenceScreen screen = manager.createPreferenceScreen(mContext);
+ final RestrictedPreference preference = new RestrictedPreference(mContext);
+ preference.setKey(mController.getPreferenceKey());
+ preference.setDisabledByAdmin(restricted
+ ? mock(RestrictedLockUtils.EnforcedAdmin.class)
+ : null);
+ assertThat(preference.isDisabledByAdmin()).isEqualTo(restricted);
+ screen.addPreference(preference);
+
+ mController.displayPreference(screen);
+ return preference;
+ }
}
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 1fed13f..e71d6a3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/DataProcessManagerTest.java
@@ -110,6 +110,7 @@
mDataProcessManager =
new DataProcessManager(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ false,
/* rawStartTimestamp= */ 0L,
@@ -130,6 +131,7 @@
final DataProcessManager dataProcessManager =
new DataProcessManager(
mContext,
+ null,
mUserIdsSeries,
/* callbackFunction= */ null);
assertThat(dataProcessManager.getShowScreenOnTime()).isFalse();
@@ -255,6 +257,7 @@
final DataProcessManager dataProcessManager =
new DataProcessManager(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ false,
/* rawStartTimestamp= */ 2L,
@@ -346,6 +349,7 @@
assertThat(
DataProcessManager.getBatteryLevelData(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ false,
/* asyncResponseDelegate= */ null))
@@ -353,6 +357,7 @@
assertThat(
DataProcessManager.getBatteryLevelData(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ true,
/* asyncResponseDelegate= */ null))
@@ -374,6 +379,7 @@
final BatteryLevelData resultData =
DataProcessManager.getBatteryLevelData(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ false,
/* asyncResponseDelegate= */ null);
@@ -402,6 +408,7 @@
final BatteryLevelData resultData =
DataProcessManager.getBatteryLevelData(
mContext,
+ null,
mUserIdsSeries,
/* isFromPeriodJob= */ false,
/* asyncResponseDelegate= */ null);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTaskTest.kt b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTaskTest.kt
new file mode 100644
index 0000000..fde45b7
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/LifecycleAwareAsyncTaskTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 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 androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import java.util.concurrent.CountDownLatch
+
+@RunWith(AndroidJUnit4::class)
+class LifecycleAwareAsyncTaskTest {
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val lifecycle = mock<Lifecycle>()
+ private val lifecycleOwner = mock<LifecycleOwner>()
+
+ @Test
+ fun addObserver_onPostExecute_onStop() {
+ val taskBeginCountDownLatch = CountDownLatch(1)
+ val taskEndCountDownLatch = CountDownLatch(1)
+ val asyncTask =
+ object : LifecycleAwareAsyncTask<Void?>(lifecycle) {
+ override fun doInBackground(vararg params: Void): Void? {
+ taskBeginCountDownLatch.await()
+ taskEndCountDownLatch.countDown()
+ return null
+ }
+ }
+
+ asyncTask.start()
+ taskBeginCountDownLatch.countDown()
+ verify(lifecycle).addObserver(asyncTask)
+
+ taskEndCountDownLatch.await()
+ asyncTask.onStop(lifecycleOwner)
+ assertThat(asyncTask.isCancelled).isTrue()
+ verify(lifecycle).removeObserver(asyncTask)
+ }
+
+ @Test
+ fun addObserver_onStop() {
+ val executorBlocker = CountDownLatch(1)
+ object : LifecycleAwareAsyncTask<Void?>(null) {
+ override fun doInBackground(vararg params: Void?): Void? {
+ executorBlocker.await()
+ return null
+ }
+ }
+ .start()
+
+ val asyncTask =
+ object : LifecycleAwareAsyncTask<Void?>(lifecycle) {
+ override fun doInBackground(vararg params: Void) = null
+ }
+
+ asyncTask.start()
+ verify(lifecycle).addObserver(asyncTask)
+
+ asyncTask.onStop(lifecycleOwner)
+ executorBlocker.countDown()
+ assertThat(asyncTask.isCancelled).isTrue()
+ verify(lifecycle).removeObserver(asyncTask)
+ }
+
+ @Test
+ fun onPostExecute_addObserver() {
+ val observers = mutableListOf<LifecycleObserver>()
+ val lifecycle =
+ object : Lifecycle() {
+ override val currentState: State
+ get() = State.RESUMED
+
+ override fun addObserver(observer: LifecycleObserver) {
+ observers.add(observer)
+ }
+
+ override fun removeObserver(observer: LifecycleObserver) {
+ observers.remove(observer)
+ }
+ }
+ val asyncTask =
+ object : LifecycleAwareAsyncTask<Void?>(lifecycle) {
+ override fun doInBackground(vararg params: Void) = null
+ }
+
+ Thread { asyncTask.start() }.start()
+ idleAsyncTaskExecutor()
+ instrumentation.waitForIdleSync()
+
+ assertThat(observers).isEmpty()
+ }
+
+ private fun idleAsyncTaskExecutor() {
+ val taskCountDownLatch = CountDownLatch(1)
+ object : LifecycleAwareAsyncTask<Void?>(null) {
+ override fun doInBackground(vararg params: Void): Void? {
+ taskCountDownLatch.countDown()
+ return null
+ }
+ }
+ .start()
+ taskCountDownLatch.await()
+ }
+
+ @Test
+ fun onStop_addObserver() {
+ val executorBlocker = CountDownLatch(1)
+ object : LifecycleAwareAsyncTask<Void?>(null) {
+ override fun doInBackground(vararg params: Void?): Void? {
+ executorBlocker.await()
+ return null
+ }
+ }
+ .start()
+
+ val asyncTask =
+ object : LifecycleAwareAsyncTask<Void?>(lifecycle) {
+ override fun doInBackground(vararg params: Void) = null
+ }
+
+ asyncTask.onStop(lifecycleOwner)
+ assertThat(asyncTask.isCancelled).isTrue()
+ verify(lifecycle).removeObserver(asyncTask)
+
+ asyncTask.start()
+ verify(lifecycle, never()).addObserver(asyncTask)
+ executorBlocker.countDown()
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
index a03ca61..1ce6c3f 100644
--- a/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/OneHandedSettingsTest.java
@@ -21,7 +21,6 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.SystemProperties;
@@ -32,8 +31,6 @@
import androidx.test.core.app.ApplicationProvider;
-import com.android.settings.R;
-import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType;
import com.android.settingslib.search.SearchIndexableRaw;
import org.junit.Before;
@@ -63,21 +60,6 @@
}
@Test
- public void getTileTooltipContent_returnsExpectedValues() {
- // Simulate to call getTileTooltipContent after onDetach
- assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT))
- .isNull();
- // Simulate to call getTileTooltipContent after onAttach
- when(mSettings.getContext()).thenReturn(mContext);
- assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT))
- .isEqualTo(mContext.getText(
- R.string.accessibility_one_handed_mode_qs_tooltip_content));
- assertThat(mSettings.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE))
- .isEqualTo(mContext.getText(
- R.string.accessibility_one_handed_mode_auto_added_qs_tooltip_content));
- }
-
- @Test
public void getLogTag_returnsCorrectTag() {
assertThat(mSettings.getLogTag()).isEqualTo("OneHandedSettings");
}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java
deleted file mode 100644
index 0a1ccbb..0000000
--- a/tests/robotests/src/com/android/settings/inputmethod/KeyboardLayoutPickerControllerTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.inputmethod;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.hardware.input.InputDeviceIdentifier;
-import android.hardware.input.InputManager;
-import android.hardware.input.KeyboardLayout;
-import android.view.InputDevice;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-import androidx.preference.PreferenceManager;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.core.BasePreferenceController;
-import com.android.settings.testutils.shadow.ShadowInputDevice;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.ShadowApplication;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = {
- com.android.settings.testutils.shadow.ShadowFragment.class,
-})
-public class KeyboardLayoutPickerControllerTest {
-
- @Mock
- private Fragment mFragment;
- @Mock
- private InputManager mInputManager;
-
- private Context mContext;
- private InputDeviceIdentifier mInputDeviceIdentifier;
- private KeyboardLayoutPickerController mController;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- final ShadowApplication shadowContext = ShadowApplication.getInstance();
- shadowContext.setSystemService(Context.INPUT_SERVICE, mInputManager);
-
- mContext = RuntimeEnvironment.application;
- mInputDeviceIdentifier = new InputDeviceIdentifier("descriptor", 1, 1);
- mController = new KeyboardLayoutPickerController(mContext, "pref_key");
-
- initializeOneLayout();
- }
-
- @Test
- public void isAlwaysAvailable() {
- assertThat(mController.getAvailabilityStatus())
- .isEqualTo(BasePreferenceController.AVAILABLE);
- }
-
- @Test
- public void testLifecycle_onStart_shouldRegisterInputManager() {
- final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
- when(mFragment.getActivity()).thenReturn(activity);
-
- mController.onStart();
-
- // Register is called, but unregister should not be called.
- verify(mInputManager).registerInputDeviceListener(mController, null);
- verify(mInputManager, never()).unregisterInputDeviceListener(mController);
- }
-
- @Test
- public void testLifecycle_onStart_NoInputDevice_shouldReturn() {
- final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
- when(mInputManager.getInputDeviceByDescriptor(anyString())).thenReturn(null);
- when(mFragment.getActivity()).thenReturn(activity);
-
- mController.onStart();
- verify(mInputManager, never()).getEnabledKeyboardLayoutsForInputDevice(any());
- }
-
- @Test
- public void testLifecycle_onStop_shouldCancelRegisterInputManager() {
- mController.onStop();
-
- // Unregister is called, but register should not be called.
- verify(mInputManager).unregisterInputDeviceListener(mController);
- verify(mInputManager, never()).registerInputDeviceListener(mController, null);
- }
-
- @Test
- public void test_createPreferenceHierarchy_shouldAddOnePreference() {
- final PreferenceManager preferenceManager = new PreferenceManager(mContext);
- final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
-
- mController.displayPreference(screen);
-
- // We create a keyboard layouts in initializeOneLayout()
- assertThat(screen.getPreferenceCount()).isEqualTo(1);
- }
-
- @Test
- public void test_createPreferenceHierarchy_shouldAddTwoPreference() {
- initializeTwoLayouts();
- final PreferenceManager preferenceManager = new PreferenceManager(mContext);
- final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
-
- mController.displayPreference(screen);
-
- // We create two keyboard layouts in initializeOneLayout()
- assertThat(screen.getPreferenceCount()).isEqualTo(2);
- }
-
- @Test
- @Config(shadows = ShadowInputDevice.class)
- public void testOnDeviceRemove_getSameDevice_shouldFinish() {
- final int TARGET_DEVICE_ID = 1;
- final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
- final String[] enableKeyboardLayouts = {"layout1"};
- final InputDevice device = ShadowInputDevice.makeInputDevicebyId(TARGET_DEVICE_ID);
-
- when(mFragment.getActivity()).thenReturn(activity);
- when(mInputManager.getInputDeviceByDescriptor(anyString())).thenReturn(device);
- when(mInputManager.getEnabledKeyboardLayoutsForInputDevice(
- any(InputDeviceIdentifier.class))).thenReturn(enableKeyboardLayouts);
-
- mController.onStart();
- mController.onInputDeviceRemoved(TARGET_DEVICE_ID);
-
- assertThat(activity.isFinishing()).isTrue();
- }
-
- @Test
- @Config(shadows = ShadowInputDevice.class)
- public void testOnDeviceRemove_getDifferentDevice_shouldNotFinish() {
- final int TARGET_DEVICE_ID = 1;
- final int ANOTHER_DEVICE_ID = 2;
- final FragmentActivity activity = Robolectric.setupActivity(FragmentActivity.class);
- final String[] enableKeyboardLayouts = {"layout1"};
- final InputDevice device = ShadowInputDevice.makeInputDevicebyId(TARGET_DEVICE_ID);
-
- when(mFragment.getActivity()).thenReturn(activity);
- when(mInputManager.getInputDeviceByDescriptor(anyString())).thenReturn(device);
- when(mInputManager.getEnabledKeyboardLayoutsForInputDevice(
- any(InputDeviceIdentifier.class))).thenReturn(enableKeyboardLayouts);
-
- mController.onStart();
- mController.onInputDeviceRemoved(ANOTHER_DEVICE_ID);
-
- assertThat(activity.isFinishing()).isFalse();
- }
-
- private void initializeOneLayout() {
- final KeyboardLayout[] keyboardLayouts = {new KeyboardLayout("", "", "", 1, null, 0, 1, 1)};
- when(mInputManager.getKeyboardLayoutsForInputDevice(
- any(InputDeviceIdentifier.class))).thenReturn(
- keyboardLayouts);
-
- mController.initialize(mFragment, mInputDeviceIdentifier);
- }
-
- private void initializeTwoLayouts() {
- final KeyboardLayout[] keyboardLayouts = {new KeyboardLayout("", "", "", 1, null, 0, 1, 1),
- new KeyboardLayout("", "", "", 2, null, 0, 2, 2)};
- when(mInputManager.getKeyboardLayoutsForInputDevice(any(InputDeviceIdentifier.class))).
- thenReturn(keyboardLayouts);
-
- mController.initialize(mFragment, mInputDeviceIdentifier);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/inputmethod/KeyboardSettingsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/KeyboardSettingsPreferenceControllerTest.java
index b8070d3..482dcd3 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/KeyboardSettingsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/KeyboardSettingsPreferenceControllerTest.java
@@ -90,7 +90,6 @@
new HardKeyboardDeviceInfo(
"TEST_DEVICE",
mInputDeviceIdentifier,
- "TEST_DEVICE_LABEL",
address,
VENDOR_ID,
PRODUCT_ID);
diff --git a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
index 9ddfd5b..014a506 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/PhysicalKeyboardPreferenceControllerTest.java
@@ -50,7 +50,6 @@
public class PhysicalKeyboardPreferenceControllerTest {
private static final String DEVICE_NAME = "deviceName";
- private static final String LAYOUT_LABEL = "deviceLayutLabel";
private static final String BLUETOOTHADDRESS = "deviceBluetoothAddress";
private static final int VENDOR_ID = 123;
private static final int PRODUCT_ID = 456;
@@ -85,7 +84,6 @@
keyboards.add(new HardKeyboardDeviceInfo(
DEVICE_NAME,
mIdentifier,
- LAYOUT_LABEL,
BLUETOOTHADDRESS,
VENDOR_ID,
PRODUCT_ID));
diff --git a/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
index 9d3cc5f..f3fe93d 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
@@ -23,12 +23,15 @@
import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.InputDevice;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.keyboard.Flags;
import com.android.settings.testutils.shadow.ShadowInputDevice;
import org.junit.Before;
@@ -48,6 +51,8 @@
})
public class TouchpadAndMouseSettingsControllerTest {
@Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
public MockitoRule rule = MockitoJUnit.rule();
private static final String PREFERENCE_KEY = "trackpad_settings";
@@ -80,6 +85,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_AND_TOUCHPAD_A11Y_NEW_PAGE_ENABLED)
public void getAvailabilityStatus_isTouchpadAvailable() {
int deviceId = 1;
ShadowInputDevice.sDeviceIds = new int[]{deviceId};
@@ -92,6 +98,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_AND_TOUCHPAD_A11Y_NEW_PAGE_ENABLED)
public void getAvailabilityStatus_isMouseAvailable() {
assumeTrue(enableVectorCursorA11ySettings());
diff --git a/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt b/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
index f064b22..da7ba5b 100644
--- a/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
+++ b/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
@@ -16,7 +16,6 @@
package com.android.settings.service
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.platform.test.annotations.RequiresFlagsEnabled
@@ -24,6 +23,7 @@
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -37,9 +37,13 @@
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterResult
-import com.android.settingslib.graph.proto.PreferenceProto
-import com.android.settingslib.graph.proto.PreferenceValueProto
-import com.android.settingslib.graph.proto.TextProto
+import com.android.settingslib.graph.preferenceGroupProto
+import com.android.settingslib.graph.preferenceOrGroupProto
+import com.android.settingslib.graph.preferenceProto
+import com.android.settingslib.graph.preferenceScreenProto
+import com.android.settingslib.graph.preferenceValueProto
+import com.android.settingslib.graph.proto.PreferenceGraphProto
+import com.android.settingslib.graph.textProto
import com.android.settingslib.graph.toProto
import com.android.settingslib.metadata.SensitivityLevel
import com.google.common.truth.Truth.assertThat
@@ -51,8 +55,76 @@
@RequiresFlagsEnabled(FLAG_SETTINGS_CATALYST)
class PreferenceServiceRequestTransformerTest {
- @get:Rule
- val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
+ @Test
+ fun transformCatalystGetMetadataResponse_emptyGraph_returnsFrameworkResponseWithError() {
+ val context: Context = ApplicationProvider.getApplicationContext()
+ val graphProto = PreferenceGraphProto.newBuilder().build()
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_UNSUPPORTED)
+ assertThat(metadataList).isEmpty()
+ }
+ }
+
+ @Test
+ fun transformCatalystGetMetadataResponse_populatedGraph_returnsFrameworkResponseWithSuccess() {
+ val context: Context = ApplicationProvider.getApplicationContext()
+ val screen = preferenceScreenProto {
+ root = preferenceGroupProto {
+ addAllPreferences(
+ listOf(
+ preferenceOrGroupProto {
+ group = preferenceGroupProto {
+ addPreferences(
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key1"
+ title = textProto { string = "title1" }
+ enabled = true
+ }
+ }
+ )
+ }
+ },
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key2"
+ title = textProto { string = "title2" }
+ enabled = false
+ }
+ },
+ )
+ )
+ }
+ }
+ val graphProto = PreferenceGraphProto.newBuilder().putScreens("screen", screen).build()
+
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_OK)
+ assertThat(metadataList.size).isEqualTo(2)
+ }
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key1" &&
+ it.screenKey == "screen" &&
+ it.title == "title1" &&
+ it.isEnabled
+ }
+ )
+ .isTrue()
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key2" &&
+ it.screenKey == "screen" &&
+ it.title == "title2" &&
+ !it.isEnabled
+ }
+ )
+ .isTrue()
+ }
@Test
fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
@@ -70,26 +142,28 @@
fun transformCatalystGetValueResponse_success_returnsValidFrameworkResponse() {
val context: Context = ApplicationProvider.getApplicationContext()
val fRequest = GetValueRequest.Builder("screen", "key").build()
- val cResult = PreferenceGetterResponse(
- emptyMap(),
- mapOf(
- PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
- PreferenceProto.newBuilder()
- .setKey("key")
- .setTitle(TextProto.newBuilder().setString("title"))
- .setSummary(TextProto.newBuilder().setString("summary"))
- .setEnabled(true)
- .setAvailable(true)
- .setRestricted(true)
- .setPersistent(true)
- .setSensitivityLevel(SensitivityLevel.LOW_SENSITIVITY)
- .setLaunchIntent(
+ val cResult =
+ PreferenceGetterResponse(
+ emptyMap(),
+ mapOf(
+ PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
+ preferenceProto {
+ key = "key"
+ title = textProto { string = "title" }
+ summary = textProto { string = "summary" }
+ enabled = true
+ available = true
+ restricted = true
+ persistent = true
+ sensitivityLevel = SensitivityLevel.LOW_SENSITIVITY
+ addReadPermissions("read_permission")
+ addWritePermissions("write_permission")
+ launchIntent =
Intent(context, SettingsHomepageActivity::class.java).toProto()
- )
- .setValue(PreferenceValueProto.newBuilder().setBooleanValue(true))
- .build()
+ value = preferenceValueProto { booleanValue = true }
+ }
+ ),
)
- )
val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
assertThat(fResult!!.resultCode).isEqualTo(GetValueResult.RESULT_OK)
with(fResult.metadata!!) {
@@ -101,6 +175,8 @@
assertThat(isWritable).isTrue()
assertThat(writeSensitivity)
.isEqualTo(SettingsPreferenceMetadata.EXPECT_POST_CONFIRMATION)
+ assertThat(readPermissions).containsExactly("read_permission")
+ assertThat(writePermissions).containsExactly("write_permission")
assertThat(launchIntent).isNotNull()
assertThat(launchIntent!!.component!!.className)
.isEqualTo(SettingsHomepageActivity::class.java.name)
@@ -115,13 +191,14 @@
fun transformCatalystGetValueResponse_failure_returnsValidFrameworkResponse() {
val context: Context = ApplicationProvider.getApplicationContext()
val fRequest = GetValueRequest.Builder("screen", "key").build()
- val cResult = PreferenceGetterResponse(
- mapOf(
- PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
+ val cResult =
+ PreferenceGetterResponse(
+ mapOf(
+ PreferenceCoordinate(fRequest.screenKey, fRequest.preferenceKey) to
PreferenceGetterErrorCode.NOT_FOUND
- ),
- emptyMap()
- )
+ ),
+ emptyMap(),
+ )
val fResult = transformCatalystGetValueResponse(context, fRequest, cResult)
with(fResult!!) {
assertThat(resultCode).isEqualTo(GetValueResult.RESULT_UNSUPPORTED)
@@ -141,13 +218,15 @@
@Test
fun transformFrameworkSetValueRequest_typeBoolean_returnsValidCatalystRequest() {
- val fRequest = SetValueRequest.Builder(
- "screen",
- "pref",
- SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_BOOLEAN)
- .setBooleanValue(true)
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_BOOLEAN)
+ .setBooleanValue(true)
+ .build(),
+ )
.build()
- ).build()
val cRequest = transformFrameworkSetValueRequest(fRequest)
with(cRequest!!) {
assertThat(screenKey).isEqualTo(fRequest.screenKey)
@@ -159,13 +238,15 @@
@Test
fun transformFrameworkSetValueRequest_typeInt_returnsValidCatalystRequest() {
- val fRequest = SetValueRequest.Builder(
- "screen",
- "pref",
- SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_INT)
- .setIntValue(5)
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_INT)
+ .setIntValue(5)
+ .build(),
+ )
.build()
- ).build()
val cRequest = transformFrameworkSetValueRequest(fRequest)
with(cRequest!!) {
assertThat(screenKey).isEqualTo(fRequest.screenKey)
@@ -177,59 +258,59 @@
@Test
fun transformFrameworkSetValueRequest_typeString_returnsNull() {
- val fRequest = SetValueRequest.Builder(
- "screen",
- "pref",
- SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_STRING)
- .setStringValue("value")
+ val fRequest =
+ SetValueRequest.Builder(
+ "screen",
+ "pref",
+ SettingsPreferenceValue.Builder(SettingsPreferenceValue.TYPE_STRING)
+ .setStringValue("value")
+ .build(),
+ )
.build()
- ).build()
val cRequest = transformFrameworkSetValueRequest(fRequest)
assertThat(cRequest).isNull()
}
@Test
fun transformCatalystSetValueResponse_returnsValidFrameworkResponse() {
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.OK).resultCode
- ).isEqualTo(SetValueResult.RESULT_OK)
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.OK).resultCode)
+ .isEqualTo(SetValueResult.RESULT_OK)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.UNAVAILABLE).resultCode)
+ .isEqualTo(SetValueResult.RESULT_UNAVAILABLE)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.DISABLED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_DISABLED)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.UNSUPPORTED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_UNSUPPORTED)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.DISALLOW).resultCode)
+ .isEqualTo(SetValueResult.RESULT_DISALLOW)
assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.UNAVAILABLE).resultCode
- ).isEqualTo(SetValueResult.RESULT_UNAVAILABLE)
+ transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_APP_PERMISSION)
+ .resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_REQUIRE_APP_PERMISSION)
assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.DISABLED).resultCode
- ).isEqualTo(SetValueResult.RESULT_DISABLED)
+ transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_USER_AGREEMENT)
+ .resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_REQUIRE_USER_CONSENT)
+
+ assertThat(transformCatalystSetValueResponse(PreferenceSetterResult.RESTRICTED).resultCode)
+ .isEqualTo(SetValueResult.RESULT_RESTRICTED)
assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.UNSUPPORTED).resultCode
- ).isEqualTo(SetValueResult.RESULT_UNSUPPORTED)
+ transformCatalystSetValueResponse(PreferenceSetterResult.INVALID_REQUEST).resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_INVALID_REQUEST)
assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.DISALLOW).resultCode
- ).isEqualTo(SetValueResult.RESULT_DISALLOW)
-
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_APP_PERMISSION)
- .resultCode
- ).isEqualTo(SetValueResult.RESULT_REQUIRE_APP_PERMISSION)
-
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.REQUIRE_USER_AGREEMENT)
- .resultCode
- ).isEqualTo(SetValueResult.RESULT_REQUIRE_USER_CONSENT)
-
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.RESTRICTED).resultCode
- ).isEqualTo(SetValueResult.RESULT_RESTRICTED)
-
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.INVALID_REQUEST).resultCode
- ).isEqualTo(SetValueResult.RESULT_INVALID_REQUEST)
-
- assertThat(
- transformCatalystSetValueResponse(PreferenceSetterResult.INTERNAL_ERROR).resultCode
- ).isEqualTo(SetValueResult.RESULT_INTERNAL_ERROR)
+ transformCatalystSetValueResponse(PreferenceSetterResult.INTERNAL_ERROR).resultCode
+ )
+ .isEqualTo(SetValueResult.RESULT_INTERNAL_ERROR)
}
-}
\ No newline at end of file
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
index 7de69a7..2741212 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowAccessibilityManager.java
@@ -16,6 +16,8 @@
package com.android.settings.testutils.shadow;
+import static com.google.common.truth.Truth.assertThat;
+
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -24,11 +26,12 @@
import android.util.ArrayMap;
import android.view.accessibility.AccessibilityManager;
-import com.android.internal.accessibility.common.ShortcutConstants;
+import com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -39,6 +42,7 @@
public class ShadowAccessibilityManager extends org.robolectric.shadows.ShadowAccessibilityManager {
private Map<ComponentName, ComponentName> mA11yFeatureToTileMap = new ArrayMap<>();
private List<AccessibilityShortcutInfo> mInstalledAccessibilityShortcutList = List.of();
+ private Map<Integer, List<String>> mShortcutTargets = new ArrayMap<>();
/**
* Implements a hidden method {@link AccessibilityManager#getA11yFeatureToTileMap}
@@ -80,7 +84,20 @@
*/
@Implementation
public List<String> getAccessibilityShortcutTargets(
- @ShortcutConstants.UserShortcutType int shortcutType) {
- return List.of();
+ @UserShortcutType int shortcutType) {
+ if (!mShortcutTargets.containsKey(shortcutType)) {
+ mShortcutTargets.put(shortcutType, new ArrayList<>());
+ }
+ List<String> targets = mShortcutTargets.get(shortcutType);
+ assertThat(targets).isNotNull();
+ return targets;
+ }
+
+ /**
+ * Used by tests to easily write directly to a shortcut targets value
+ */
+ public void setAccessibilityShortcutTargets(
+ @UserShortcutType int shortcutType, List<String> targets) {
+ mShortcutTargets.put(shortcutType, targets);
}
}
diff --git a/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt b/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt
new file mode 100644
index 0000000..31ee9e6
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 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.spa.wifi.dpp
+
+import android.app.KeyguardManager
+import android.content.Context
+import android.hardware.biometrics.BiometricPrompt
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.settings.wifi.dpp.WifiDppUtils
+import java.security.InvalidKeyException
+import java.security.Key
+import javax.crypto.Cipher
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoSession
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class WifiDppUtilsTest {
+ private lateinit var mockSession: MockitoSession
+
+ private val runnable = mock<Runnable>()
+ private val cipher = mock<Cipher>()
+ private var mockKeyguardManager = mock<KeyguardManager>()
+ private var context: Context =
+ spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(KeyguardManager::class.java) } doReturn mockKeyguardManager
+ }
+
+ @Before
+ fun setUp() {
+ mockSession =
+ ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .mockStatic(Cipher::class.java)
+ .mockStatic(BiometricPrompt::class.java)
+ .mockStatic(BiometricPrompt.Builder::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ whenever(context.applicationContext).thenReturn(context)
+ }
+
+ @After
+ fun tearDown() {
+ mockSession.finishMocking()
+ }
+
+ @Test
+ fun showLockScreen_notKeyguardSecure_runRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn false }
+
+ WifiDppUtils.showLockScreen(context, runnable)
+
+ verify(runnable).run()
+ }
+
+ @Test
+ fun showLockScreen_isKeyguardSecure_doNotRunRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+
+ try {
+ WifiDppUtils.showLockScreen(context, runnable)
+ } catch (_: Exception) {}
+
+ verify(runnable, never()).run()
+ }
+
+ @Test
+ fun showLockScreenForWifiSharing_deviceUnlockedRecently_runRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+ whenever(Cipher.getInstance(WifiDppUtils.AES_CBC_PKCS7_PADDING)).thenReturn(cipher)
+
+ WifiDppUtils.showLockScreenForWifiSharing(context, runnable)
+
+ verify(runnable).run()
+ }
+
+ @Test
+ fun showLockScreenForWifiSharing_deviceNotUnlockedRecently_doNotRunRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+ whenever(Cipher.getInstance(WifiDppUtils.AES_CBC_PKCS7_PADDING)).thenReturn(cipher)
+ doThrow(InvalidKeyException()).whenever(cipher).init(anyInt(), any<Key>())
+
+ try {
+ WifiDppUtils.showLockScreenForWifiSharing(context, runnable)
+ } catch (_: Exception) {}
+
+ verify(runnable, never()).run()
+ }
+}
diff --git a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
index c3ccf34..96d9583 100644
--- a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
@@ -24,20 +24,29 @@
import android.content.Context;
import android.content.res.Resources;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.R;
+import com.android.server.display.feature.flags.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+/** Tests for {@link ReduceBrightColorsIntensityPreferenceController} */
@RunWith(AndroidJUnit4.class)
public class ReduceBrightColorsIntensityPreferenceControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
private Context mContext;
private Resources mResources;
private ReduceBrightColorsIntensityPreferenceController mPreferenceController;
@@ -52,27 +61,119 @@
}
@Test
- public void isAvailable_configuredRbcAvailable_enabledRbc_shouldReturnTrue() {
+ @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOffAndDisabled_RbcOnAndAvailable_returnTrue() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
doReturn(true).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
+
assertThat(mPreferenceController.isAvailable()).isTrue();
}
+
@Test
- public void isAvailable_configuredRbcAvailable_disabledRbc_shouldReturnTrue() {
+ @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOffAndDisabled_RbcOffAndAvailable_returnTrue() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
doReturn(true).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
+
assertThat(mPreferenceController.isAvailable()).isTrue();
}
+
@Test
- public void isAvailable_configuredRbcUnavailable_enabledRbc_shouldReturnFalse() {
+ @DisableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOffAndDisabled_RbcOnAndUnavailable_returnFalse() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
doReturn(false).when(mResources).getBoolean(
R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndDisabled_RbcOnAndAvailable_returnTrue() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndDisabled_RbcOffAndAvailable_returnTrue() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndDisabled_RbcOnAndUnavailable_returnFalse() {
+ doReturn(false).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ doReturn(false).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndEnabled_RbcOnAndAvailable_returnFalse() {
+ doReturn(true).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndEnabled_RbcOffAndAvailable_returnFalse() {
+ doReturn(true).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 0);
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
+ assertThat(mPreferenceController.isAvailable()).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_EVEN_DIMMER)
+ public void isAvailable_whenEvenDimmerOnAndEnabled_RbcOnAndUnavailable_returnFalse() {
+ doReturn(true).when(mResources).getBoolean(
+ com.android.internal.R.bool.config_evenDimmerEnabled);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
+ doReturn(false).when(mResources).getBoolean(
+ R.bool.config_reduceBrightColorsAvailable);
+
assertThat(mPreferenceController.isAvailable()).isFalse();
}
diff --git a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
index 4c07555..a31b0f0 100644
--- a/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
+++ b/tests/unit/src/com/android/settings/applications/appcompat/UserAspectRatioManagerTest.java
@@ -31,7 +31,7 @@
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN;
import static com.android.settings.applications.appcompat.UserAspectRatioManager.KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS;
-import static com.android.window.flags.Flags.FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT;
+import static com.android.window.flags.Flags.FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT;
import static com.google.common.truth.Truth.assertThat;
@@ -54,6 +54,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
@@ -102,7 +103,6 @@
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getSystemService(LauncherApps.class)).thenReturn(launcherApps);
enableAllDefaultAspectRatioOptions();
- mSetFlagsRule.disableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
@@ -318,19 +318,6 @@
assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm));
}
- @Test
- public void testGetUserMinAspectRatioMapping_appDefaultFlagEnabled() {
- // Flag is disabled by default, app default not loaded
- assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
-
- mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
- mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
-
- assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName));
- assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName))
- .isEqualTo(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName));
- }
-
private void assertUnsetIsFullscreen() {
// Fullscreen option is pre-selected
assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName))
@@ -406,14 +393,8 @@
}
@Test
- public void testIsOverrideToFullscreenEnabled_flagDisabled_returnsFalse() {
- mUtils.setFullscreenCompatChange(true);
- assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
- }
-
- @Test
+ @DisableFlags({FLAG_UNIVERSAL_RESIZABLE_BY_DEFAULT})
public void testIsOverrideToFullscreenEnabledUnivRes_flagDisabled_returnsFalse() {
- mUtils.setFullscreenCompatChange(true);
assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId()));
}
@@ -426,20 +407,12 @@
}
private void setIsOverrideToFullscreenEnabledBecauseCompatChange(boolean enabled) {
- if (enabled) {
- mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
- mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
- }
mUtils.setFullscreenCompatChange(enabled);
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
.thenReturn(enabled);
}
private void setIsOverrideToFullscreenEnabledBecauseUniversalResizeable(boolean enabled) {
- if (enabled) {
- mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT);
- mUtils = new FakeUserAspectRatioManager(mContext, mIPm);
- }
mUtils.setUniversalResizeable(enabled);
when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName))
.thenReturn(enabled);
diff --git a/tests/unit/src/com/android/settings/language/LanguagePreferenceControllerTest.java b/tests/unit/src/com/android/settings/language/LanguagePreferenceControllerTest.java
index 656fa27..2209355 100644
--- a/tests/unit/src/com/android/settings/language/LanguagePreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/language/LanguagePreferenceControllerTest.java
@@ -21,18 +21,24 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.Settings;
+import com.android.settings.flags.Flags;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
public class LanguagePreferenceControllerTest {
private Context mContext;
private LanguagePreferenceController mController;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
@Before
public void setup() {
mContext = ApplicationProvider.getApplicationContext();
@@ -40,6 +46,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_REGIONAL_PREFERENCES_API_ENABLED)
public void getAvailabilityStatus_featureFlagOff_LanguageSettingsActivitydisabled() {
mController.getAvailabilityStatus();
diff --git a/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt b/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt
index 27b5c38..c6974ab 100644
--- a/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt
+++ b/tests/unit/src/com/android/settings/network/telephony/CellInfoUtilTest.kt
@@ -24,6 +24,7 @@
import com.android.settings.network.telephony.CellInfoUtil.getNetworkTitle
import com.android.settings.network.telephony.CellInfoUtil.getOperatorNumeric
import com.google.common.truth.Truth.assertThat
+import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
@@ -95,6 +96,7 @@
}
@Test
+ @Ignore("b/383858953")
fun cellInfoListToString() {
val cellInfoList =
listOf(