Merge "Show/hide 5G preferred network type also consider allowed_network_type"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9bb810d..1f9c650 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1193,6 +1193,15 @@
android:value="com.android.settings.notification.history.NotificationStation" />
</activity>
+ <activity
+ android:name=".notification.history.NotificationHistoryActivity"
+ android:label="@string/notification_history_title">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".notification.zen.ZenModeVoiceActivity"
android:theme="@*android:style/Theme.DeviceDefault.Settings.Dialog.NoActionBar"
android:label="@string/zen_mode_settings_title">
@@ -2858,8 +2867,7 @@
<activity
android:name="Settings$AppManageExternalStorageActivity"
- android:label="@string/manage_external_storage_title"
- android:permission="android.permission.MANAGE_EXTERNAL_STORAGE">
+ android:label="@string/manage_external_storage_title">
<intent-filter android:priority="1">
<action android:name="android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png b/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png
new file mode 100644
index 0000000..a8d86aa
--- /dev/null
+++ b/res/drawable/accessibility_shortcut_type_software_gesture_talkback.png
Binary files differ
diff --git a/res/drawable/ic_clear.xml b/res/drawable/ic_clear.xml
new file mode 100644
index 0000000..224425f
--- /dev/null
+++ b/res/drawable/ic_clear.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!--
+ 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:width="24dp"
+ android:height="24dp">
+ <path
+ android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12Z"
+ android:fillColor="#FFFFFF" />
+</vector>
+
diff --git a/res/drawable/ic_snooze.xml b/res/drawable/ic_snooze.xml
new file mode 100644
index 0000000..d7bbbaf
--- /dev/null
+++ b/res/drawable/ic_snooze.xml
@@ -0,0 +1,24 @@
+<!--
+ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9,11h3.63L9,15.2L9,17h6v-2h-3.63L15,10.8L15,9L9,9v2zM16.056,3.346l1.282,-1.535 4.607,3.85 -1.28,1.54zM3.336,7.19l-1.28,-1.536L6.662,1.81l1.28,1.536zM12,6c3.86,0 7,3.14 7,7s-3.14,7 -7,7 -7,-3.14 -7,-7 3.14,-7 7,-7m0,-2c-4.97,0 -9,4.03 -9,9s4.03,9 9,9 9,-4.03 9,-9 -4.03,-9 -9,-9z"/>
+</vector>
diff --git a/res/drawable/ic_today.xml b/res/drawable/ic_today.xml
new file mode 100644
index 0000000..1d39610
--- /dev/null
+++ b/res/drawable/ic_today.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,3h-1L18,1h-2v2L8,3L8,1L6,1v2L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19L5,8h14v11zM9,9.5c-1.38,0 -2.5,1.12 -2.5,2.5s1.12,2.5 2.5,2.5 2.5,-1.12 2.5,-2.5S10.38,9.5 9,9.5z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/rounded_bg.xml b/res/drawable/rounded_bg.xml
new file mode 100644
index 0000000..fcdd761
--- /dev/null
+++ b/res/drawable/rounded_bg.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius"
+ />
+</shape>
\ No newline at end of file
diff --git a/res/layout/accessibility_edit_shortcut_component.xml b/res/layout/accessibility_edit_shortcut_component.xml
index 52b8935..dd92a1d 100644
--- a/res/layout/accessibility_edit_shortcut_component.xml
+++ b/res/layout/accessibility_edit_shortcut_component.xml
@@ -26,20 +26,25 @@
android:id="@+id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:saveEnabled="false"/>
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:saveEnabled="false"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/textColorAlertDialogListItem" />
<TextView
android:id="@+id/summary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:textColor="?android:attr/textColorSecondary"
- android:paddingStart="32dp"/>
+ android:paddingBottom="8dp"
+ android:paddingStart="32dp"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="32dp"
- android:scaleType="fitCenter"/>
+ android:scaleType="fitCenter" />
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/layout/accessibility_edit_shortcut_magnification.xml b/res/layout/accessibility_edit_shortcut_magnification.xml
index 76a1fad..8a724cc 100644
--- a/res/layout/accessibility_edit_shortcut_magnification.xml
+++ b/res/layout/accessibility_edit_shortcut_magnification.xml
@@ -52,8 +52,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:textColor="?android:attr/colorAccent"
- android:text="@string/accessibility_shortcut_edit_dialog_title_advance"/>
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:text="@string/accessibility_shortcut_edit_dialog_title_advance"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:textColor="?android:attr/colorAccent" />
</LinearLayout>>
diff --git a/res/layout/contextual_slice_deferred_setup.xml b/res/layout/contextual_slice_deferred_setup.xml
deleted file mode 100644
index 7d5b688..0000000
--- a/res/layout/contextual_slice_deferred_setup.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2019 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<com.google.android.material.card.MaterialCardView
- xmlns:android="http://schemas.android.com/apk/res/android"
- style="@style/ContextualCardStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="start"
- android:orientation="vertical"
- android:paddingEnd="@dimen/contextual_card_padding_end"
- android:paddingTop="@dimen/contextual_deferred_setup_card_padding_top"
- android:paddingBottom="@dimen/contextual_deferred_setup_card_padding_bottom">
-
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="@dimen/contextual_card_icon_size"
- android:layout_height="@dimen/contextual_card_icon_size"
- android:layout_marginStart="@dimen/contextual_card_icon_padding_start"/>
-
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/contextual_card_text_padding_start"
- android:layout_marginTop="@dimen/contextual_deferred_setup_card_title_margin_top"
- android:ellipsize="end"
- android:maxLines="2"
- android:minLines="1"
- android:textAppearance="@style/TextAppearance.DeferredSetupCardTitle"/>
-
- <TextView
- android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/contextual_card_text_padding_start"
- android:layout_marginTop="@dimen/contextual_deferred_setup_card_summary_margin_top"
- android:ellipsize="end"
- android:maxLines="2"
- android:minLines="1"
- android:textAppearance="@style/TextAppearance.DeferredSetupCardSummary"/>
-
- <Button
- android:id="@+id/finish_setup"
- style="@style/DeferredSetupCardButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/contextual_card_text_padding_start"
- android:layout_marginTop="@dimen/contextual_deferred_setup_card_button_margin_top"
- android:text="@string/suggestion_button_text"/>
-
- </LinearLayout>
-</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
diff --git a/res/layout/contextual_slice_sticky_tile.xml b/res/layout/contextual_slice_sticky_tile.xml
new file mode 100644
index 0000000..8e82f53
--- /dev/null
+++ b/res/layout/contextual_slice_sticky_tile.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<com.google.android.material.card.MaterialCardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/ContextualCardStyle">
+
+ <androidx.slice.widget.SliceView
+ android:id="@+id/slice_view"
+ style="@style/ContextualCardSliceViewStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:importantForAccessibility="no"/>
+
+</com.google.android.material.card.MaterialCardView>
\ No newline at end of file
diff --git a/res/layout/notification_history.xml b/res/layout/notification_history.xml
new file mode 100644
index 0000000..91a59a8
--- /dev/null
+++ b/res/layout/notification_history.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/scroll"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:background="@*android:color/material_grey_50">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <!-- TODO: header switch -->
+ <LinearLayout
+ android:id="@+id/snoozed_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginBottom="10dp">
+ <LinearLayout
+ android:id="@+id/snooze_header"
+ android:layout_height="48dp"
+ android:layout_width="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:src="@drawable/ic_snooze"
+ android:tint="?android:attr/textColorPrimary"
+ android:padding="6dp"
+ android:layout_height="36dp"
+ android:layout_width="36dp" />
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/notification_history_snooze"
+ android:textColor="?android:attr/textColorPrimary"
+ android:paddingStart="@dimen/notification_history_header_drawable_start" />
+ </LinearLayout>
+ <FrameLayout
+ android:id="@+id/list_container"
+ android:layout_width="wrap_content"
+ android:layout_height="300dp"
+ android:clipChildren="true"
+ android:elevation="3dp"
+ android:background="@drawable/rounded_bg">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/notification_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ settings:fastScrollEnabled="true"
+ settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+ settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+ </FrameLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/recently_dismissed_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginBottom="10dp">
+ <LinearLayout
+ android:id="@+id/dismissed_header"
+ android:layout_height="48dp"
+ android:layout_width="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:src="@drawable/ic_clear"
+ android:tint="?android:attr/textColorPrimary"
+ android:padding="6dp"
+ android:layout_height="36dp"
+ android:layout_width="36dp" />
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/notification_history_dismiss"
+ android:textColor="?android:attr/textColorPrimary"
+ android:paddingStart="@dimen/notification_history_header_drawable_start" />
+ </LinearLayout>
+ <FrameLayout
+ android:id="@+id/list_container"
+ android:layout_width="wrap_content"
+ android:layout_height="300dp"
+ android:elevation="3dp"
+ android:clipChildren="true"
+ android:background="@drawable/rounded_bg">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/notification_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ settings:fastScrollEnabled="true"
+ settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+ settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+ </FrameLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/today_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/app_header"
+ android:layout_height="48dp"
+ android:layout_width="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+ <ImageView
+ android:src="@drawable/ic_today"
+ android:tint="?android:attr/textColorPrimary"
+ android:padding="6dp"
+ android:layout_height="36dp"
+ android:layout_width="36dp" />
+ <TextView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/notification_history_today"
+ android:textColor="?android:attr/textColorPrimary"
+ android:paddingStart="@dimen/notification_history_header_drawable_start" />
+ </LinearLayout>
+ <LinearLayout
+ android:id="@+id/apps"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:elevation="3dp"
+ android:orientation="vertical"
+ android:background="@drawable/rounded_bg">
+ <!-- app based recycler views added here -->
+ </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
+</ScrollView>
\ No newline at end of file
diff --git a/res/layout/notification_history_app_layout.xml b/res/layout/notification_history_app_layout.xml
new file mode 100644
index 0000000..6f26789
--- /dev/null
+++ b/res/layout/notification_history_app_layout.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <!-- 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:id="@+id/app_header"
+ android:layout_height="48dp"
+ android:layout_width="match_parent">
+ <ImageView
+ android:id="@+id/icon"
+ android:padding="6dp"
+ android:layout_centerVertical="true"
+ android:layout_height="36dp"
+ android:layout_width="36dp" />
+ <TextView
+ android:id="@+id/label"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+
+ android:textColor="@color/material_grey_900"
+ android:layout_toEndOf="@id/icon"
+ android:paddingStart="@dimen/notification_history_header_drawable_start" />
+ <ImageButton
+ android:id="@+id/expand"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_height="48dp"
+ android:layout_width="48dp"
+ android:src="@*android:drawable/ic_expand_more"/>
+ </RelativeLayout>
+
+ <FrameLayout
+ android:id="@+id/list_container"
+ android:layout_width="match_parent"
+ android:clipChildren="true"
+ android:layout_height="300dp">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/notification_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="true"
+ settings:fastScrollEnabled="true"
+ settings:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollHorizontalTrackDrawable="@drawable/line_drawable"
+ settings:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
+ settings:fastScrollVerticalTrackDrawable="@drawable/line_drawable"/>
+
+ </FrameLayout>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="0.5dp"
+ android:background="@color/material_grey_300" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/notification_history_log_row.xml b/res/layout/notification_history_log_row.xml
new file mode 100644
index 0000000..4c38167
--- /dev/null
+++ b/res/layout/notification_history_log_row.xml
@@ -0,0 +1,93 @@
+<!--
+ 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="6dp"
+ android:paddingTop="6dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="vertical"
+ android:background="?android:attr/selectableItemBackground">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginBottom="6dp">
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="@color/material_grey_900"
+ android:textAlignment="viewStart"/>
+
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@*android:dimen/status_bar_icon_size"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="6dp"
+ android:paddingTop="1dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:layout_toEndOf="@id/title"
+ android:tint="?android:attr/textColorSecondary"
+ android:src="@drawable/ic_notifications_alert"/>
+
+ <DateTimeView
+ android:id="@+id/timestamp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0.5dp"
+ android:layout_marginTop="17dp"
+ android:background="@color/material_grey_300" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/notification_sbn_log_row.xml b/res/layout/notification_sbn_log_row.xml
new file mode 100644
index 0000000..3c51abd
--- /dev/null
+++ b/res/layout/notification_sbn_log_row.xml
@@ -0,0 +1,142 @@
+<!--
+ 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="6dp"
+ android:paddingTop="6dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="vertical"
+ android:background="?android:attr/selectableItemBackground"
+ >
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="@*android:dimen/status_bar_icon_size"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/pkgicon"
+ android:layout_marginStart="0dp"
+ android:layout_marginEnd="8dp"
+ android:contentDescription="@null"
+ android:adjustViewBounds="true"
+ android:tint="?android:attr/textColorPrimary"
+ android:maxHeight="@*android:dimen/status_bar_icon_size"
+ android:maxWidth="@*android:dimen/status_bar_icon_size"
+ android:scaleType="fitCenter" />
+
+ <TextView
+ android:id="@+id/pkgname"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/icon"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textStyle="bold"
+ android:textAlignment="viewStart" />
+
+ <ImageView
+ android:id="@+id/alerted_icon"
+ android:layout_width="@*android:dimen/status_bar_icon_size"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_marginStart="6dp"
+ android:paddingTop="1dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:layout_toEndOf="@id/pkgname"
+ android:tint="?android:attr/textColorSecondary"
+ android:src="@drawable/ic_notifications_alert"
+ />
+
+ <ImageView
+ android:id="@+id/profile_badge"
+ android:layout_width="@*android:dimen/status_bar_icon_size"
+ android:layout_height="@*android:dimen/status_bar_icon_size"
+ android:layout_centerVertical="true"
+ android:layout_marginEnd="6dp"
+ android:paddingTop="1dp"
+ android:scaleType="fitCenter"
+ android:visibility="gone"
+ android:layout_toStartOf="@id/timestamp"
+ />
+
+ <DateTimeView
+ android:id="@+id/timestamp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp"
+ android:layout_alignBottom="@android:id/widget_frame"
+ android:layout_alignParentEnd="true"
+ android:layout_alignTop="@android:id/widget_frame"
+ android:layout_centerVertical="true"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginStart="30dp"
+ android:layout_marginBottom="6dp"
+ >
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAlignment="viewStart"
+ />
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left|center_vertical"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="0.5dp"
+ android:layout_marginTop="17dp"
+ android:background="@color/material_grey_300" />
+</LinearLayout>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 4440358..396a485 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -106,6 +106,8 @@
<item>@string/dark_ui_auto_mode_never</item>
<!-- 1: Auto -->
<item>@string/dark_ui_auto_mode_auto</item>
+ <!-- 2: Custom -->
+ <item>@string/dark_ui_auto_mode_custom</item>
</string-array>
<!-- Security settings. The delay after screen is turned off until device locks.
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b8afd60..f79874c 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -81,6 +81,8 @@
<dimen name="notification_importance_button_text">16sp</dimen>
<dimen name="notification_importance_button_padding">14dp</dimen>
+ <dimen name="notification_history_header_drawable_start">26dp</dimen>
+
<dimen name="zen_mode_button_padding_vertical">16dp</dimen>
<dimen name="zen_schedule_rule_checkbox_padding">7dp</dimen>
<dimen name="zen_schedule_day_margin">17dp</dimen>
@@ -342,15 +344,6 @@
<dimen name="contextual_half_card_padding_top">12dp</dimen>
<dimen name="contextual_half_card_padding_bottom">16dp</dimen>
<dimen name="contextual_half_card_title_margin_top">12dp</dimen>
- <dimen name="contextual_deferred_setup_card_padding_top">16dp</dimen>
- <dimen name="contextual_deferred_setup_card_padding_bottom">12dp</dimen>
- <dimen name="contextual_deferred_setup_card_title_margin_top">12dp</dimen>
- <dimen name="contextual_deferred_setup_card_summary_margin_top">2dp</dimen>
- <dimen name="contextual_deferred_setup_card_button_margin_top">8dp</dimen>
- <dimen name="contextual_deferred_setup_card_button_padding_top">8dp</dimen>
- <dimen name="contextual_deferred_setup_card_button_padding_bottom">8dp</dimen>
- <dimen name="contextual_deferred_setup_card_button_padding_start">24dp</dimen>
- <dimen name="contextual_deferred_setup_card_button_padding_end">24dp</dimen>
<!-- Homepage dismissal cards size and padding -->
<dimen name="contextual_card_dismissal_margin_top">12dp</dimen>
@@ -400,6 +393,8 @@
<!-- Maximum height for SliceView, override on slices/view/src/main/res/values/dimens.xml -->
<!-- A single Row Slice height is 60dp -->
<dimen name="abc_slice_large_height">1200dp</dimen>
+ <!-- Min height of slice row view, override on slices/view/src/main/res/values/dimens.xml -->
+ <dimen name="abc_slice_row_min_height">@dimen/abc_slice_row_max_height</dimen>
<!-- System navigation settings illustration height -->
<dimen name="system_navigation_illustration_height">320dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 330ada4..eede918 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -144,8 +144,6 @@
<string name="bluetooth_lock_voice_dialing_summary">
Prevent use of the bluetooth dialer when the screen is locked
</string>
- <!-- Bluetooth settings screen, heading above the list of nearby bluetooth devices -->
- <string name="bluetooth_devices">Bluetooth devices</string>
<!-- Bluetooth settings screen, title for the current bluetooth name setting -->
<string name="bluetooth_device_name">Device name</string>
<!-- Bluetooth settings screen, image description for device details button. This opens the screen to rename, unpair, etc. a single device. -->
@@ -2764,7 +2762,7 @@
<string name="night_display_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
<!-- Display settings screen, summary of night display when off and will *never* turn on automatically. [CHAR LIMIT=NONE] -->
<string name="night_display_summary_off_auto_mode_never">Will never turn on automatically</string>
- <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
+ <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
<string name="night_display_summary_off_auto_mode_custom">Will turn on automatically at <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
<!-- Display settings screen, summary of night display when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
<string name="night_display_summary_off_auto_mode_twilight">Will turn on automatically at sunset</string>
@@ -2806,9 +2804,11 @@
<string name="dark_ui_auto_mode_title">Schedule</string>
<!-- Dark UI screen, setting option value for Dark UI to *never* turn on/off automatically. [CHAR LIMIT=30] -->
<string name="dark_ui_auto_mode_never">None</string>
- <!-- Dark UIscreen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
+ <!-- Dark UI screen, setting option value for Dark UI to turn on/off automatically at sunset/sunrise. [CHAR LIMIT=32] -->
<string name="dark_ui_auto_mode_auto">Turns on from sunset to sunrise</string>
- <!-- Dark UIscreen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
+ <!-- Dark UI screen, setting option value for Dark theme to turn on/off automatically according to a user defined schedule. [CHAR LIMIT=32] -->
+ <string name="dark_ui_auto_mode_custom">Turns on at custom time</string>
+ <!-- Dark UI screen, setting option name controlling the current activation status. [CHAR LIMIT=30] -->
<string name="dark_ui_status_title">Status</string>
<!-- Display settings screen, summary format of Dark UI when off. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_off">Off / <xliff:g name="auto_mode_summary" example="Never turn on automatically">%1$s</xliff:g></string>
@@ -2816,12 +2816,20 @@
<string name="dark_ui_summary_off_auto_mode_never">Will never turn on automatically</string>
<!-- Display settings screen, summary of Dark UI when off and will turn on automatically at sunset. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_off_auto_mode_auto">Will turn on automatically at sunset</string>
+ <!-- Display settings screen, summary format of night display when off and will turn on automatically at a user defined time. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_off_auto_mode_custom">Will turn on automatically at <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
<!-- Display settings screen, summary format of Dark UI when on. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on">On / <xliff:g name="auto_mode_summary" example="Never turn off automatically">%1$s</xliff:g></string>
<!-- Display settings screen, summary of Dark UI when on and will *never* turn off automatically. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on_auto_mode_never">Will never turn off automatically</string>
<!-- Display settings screen, summary of Dark UI when on and will turn off automatically at sunrise. [CHAR LIMIT=NONE] -->
<string name="dark_ui_summary_on_auto_mode_auto">Will turn off automatically at sunrise</string>
+ <!-- Display settings screen, summary format of night display when on and will turn off automatically at a user defined time. [CHAR LIMIT=NONE] -->
+ <string name="dark_ui_summary_on_auto_mode_custom">Will turn off automatically at <xliff:g name="time" example="10 PM">%1$s</xliff:g></string>
+ <!-- Display settings screen, activation button action for custom schedule [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_on_custom">Turn on until <xliff:g name="time" example="6 AM">%1$s</xliff:g></string>
+ <!-- Display settings screen, deactivation button action for custom schedule [CHAR LIMIT=40] -->
+ <string name="dark_ui_activation_off_custom">Turn off until <xliff:g name="time" example="10 PM">%1$s</xliff:g></string>
<!-- Dark theme screen, description of Dark theme feature. [CHAR LIMIT=NONE] -->
<string name="dark_ui_text">Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off.</string>
@@ -4795,8 +4803,8 @@
<string name="accessibility_screen_magnification_navbar_title">Magnify with shortcut</string>
<!-- Summary for the accessibility magnification setting indicating both "Magnify with button" and "Magnify with triple-tap" are enabled [CHAR LIMIT=50] -->
<string name="accessibility_screen_magnification_state_navbar_gesture">Magnify with shortcut & triple-tap</string>
- <!-- Title for the footer text to explain what Magnify does. [CHAR LIMIT=35] -->
- <string name="accessibility_screen_magnification_about">About Magnify</string>
+ <!-- Title for the footer text to explain what accessibility service does. [CHAR LIMIT=35] -->
+ <string name="accessibility_footer_title">About <xliff:g id="service" example="Select to Speak">%1$s</xliff:g></string>
<!-- Title for the footer text to explain what option accessibility service does. [CHAR LIMIT=35] -->
<string name="accessibility_screen_option">Options</string>
<!-- Summary for the accessibility preference to enable screen magnification. [CHAR LIMIT=25] -->
@@ -4818,13 +4826,13 @@
<!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using the 3-button nav bar. [CHAR LIMIT=NONE] -->
<string name="accessibility_tutorial_dialog_message_button">To turn this service on or off, tap the accessibility button <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> on the bottom of your screen.\n\nTo switch between services, touch & hold the accessibility button.</string>
<!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is not enabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_tutorial_dialog_message_gesture_without_talkback">To turn this service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
+ <string name="accessibility_tutorial_dialog_message_gesture">To turn this service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
<!-- Message for the Accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is enabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_tutorial_dialog_message_gesture_with_talkback">To turn this service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
+ <string name="accessibility_tutorial_dialog_message_gesture_talkback">To turn this service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
<!-- Message for the Accessibility tutorial dialog when user chooses gesture navigation in navigation settings, an accessibility service is using the accessibility button, and touch exploration is disabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_tutorial_dialog_message_gesture_settings_without_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
+ <string name="accessibility_tutorial_dialog_message_gesture_settings">To turn an accessibility service on or off, swipe up from the bottom of the screen with two fingers.\n\nTo switch between services, swipe up with two fingers and hold.</string>
<!-- Message for the Accessibility tutorial dialog when user chooses gesture navigation in navigation settings, an accessibility service is using the accessibility button, and touch exploration is enabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_tutorial_dialog_message_gesture_settings_with_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
+ <string name="accessibility_tutorial_dialog_message_gesture_settings_talkback">To turn an accessibility service on or off, swipe up from the bottom of the screen with three fingers.\n\nTo switch between services, swipe up with three fingers and hold.</string>
<!-- Button for the Accessibility tutorial dialog to dismiss the dialog when user clicks it. [CHAR LIMIT=10] -->
<string name="accessibility_tutorial_dialog_button">Got it</string>
<!-- Title for accessibility shortcut preference for accessibility apps. [CHAR LIMIT=40] -->
@@ -4839,12 +4847,16 @@
<string name="accessibility_shortcut_edit_dialog_title_daltonizer">Shortcut to open Color correction</string>
<!-- Title for software shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_software">Accessibility Button</string>
- <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+ <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation is enabled. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_software_gesture">2-finger swipe up from bottom</string>
+ <!-- Title for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_dialog_title_software_gesture_talkback">3-finger swipe up from bottom</string>
<!-- Summary for software shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_summary_software">Tap the <xliff:g id="accessibility_icon" example="[Icon]">%s</xliff:g> button at the bottom of your screen</string>
- <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+ <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation is enabled. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture">Swipe up with 2 fingers from edge of screen</string>
+ <!-- Summary for software shortcut in gesture mode in Accessibility edit shortcut dialog while using gesture navigation and touch exploration are enabled [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_edit_dialog_summary_software_gesture_talkback">Swipe up with 3 fingers from edge of screen</string>
<!-- Title for hardware shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_hardware">Hold volume keys</string>
<!-- Summary for hardware shortcut in Accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
@@ -4948,7 +4960,7 @@
<!-- Used in the accessibility service settings to control turning on/off the service entirely -->
<string name="accessibility_service_master_switch_title">Use <xliff:g id="accessibility_app_name" example="TalkBack">%1$s</xliff:g></string>
<!-- Used in the Color correction settings screen to control turning on/off the feature entirely -->
- <string name="accessibility_daltonizer_master_switch_title">Use color correction</string>
+ <string name="accessibility_daltonizer_master_switch_title">Correct colors</string>
<!-- Used in the Captions settings screen to control turning on/off the feature entirely -->
<string name="accessibility_caption_master_switch_title">Show captions</string>
<!-- Title for Caption preference settings screen for configuring font style. [CHAR LIMIT=NONE] -->
@@ -6161,6 +6173,11 @@
<string name="admin_more_details">More details</string>
<string name="notification_log_title">Notification log</string>
+ <string name="notification_history_title">Notification history</string>
+ <string name="notification_history_today">Today</string>
+ <string name="notification_history_snooze">Snoozed</string>
+ <string name="notification_history_dismiss">Recently dismissed</string>
+
<!-- Category title for phone call's ringtone and vibration settings in the Sound Setting.
[CHAR LIMIT=40] -->
<string name="sound_category_call_ringtone_vibrate_title">Call ringtone & vibrate</string>
@@ -9061,13 +9078,21 @@
</plurals>
<!-- Explanation that the app that will ALWAYS be launched to open web links to domains that it understands -->
- <string name="app_link_open_always">Open in this app</string>
+ <string name="app_link_open_always">Allow app to open supported links</string>
<!-- Explanation that the user will be asked whether to launch the app to open web links to domains that it understands -->
<string name="app_link_open_ask">Ask every time</string>
<!-- Explanation that the app that will NEVER be launched to open web links to domains that it understands -->
- <string name="app_link_open_never">Don’t open in this app</string>
+ <string name="app_link_open_never">Don’t allow app to open links</string>
+
+ <plurals name="app_link_open_always_summary">
+ <item quantity="one">App claims to handle <xliff:g id="count">%d</xliff:g> link</item>
+ <item quantity="other">App claims to handle <xliff:g id="count">%d</xliff:g> links</item>
+ </plurals>
+
+ <!-- Footer of open supported links settings [CHAR LIMIT=NONE] -->
+ <string name="open_supported_links_footer">App claims to handle following links:</string>
<!-- Title for Default Apps settings [CHAR LIMIT=30] -->
<string name="default_apps_title">Default</string>
@@ -10797,13 +10822,15 @@
<!-- Title for App Compatibility Changes dashboard where developers can configure per-app overrides for compatibility changes [CHAR LIMIT=50] -->
<string name="platform_compat_dashboard_title">App Compatibility Changes</string>
<!-- Summary for App Compatibility Changes dashboard [CHAR LIMIT=NONE] -->
- <string name="platform_compat_dashboard_summary">Modify app compatibility change overrides</string>
+ <string name="platform_compat_dashboard_summary">Toggle app compatibility changes</string>
+ <!-- Summary for selected app [DO NOT TRANSLATE] -->
+ <string name="platform_compat_selected_app_summary" translatable="false"><xliff:g id="app_name" example="com.google.android.chrome">%1$s</xliff:g> targetSdkVersion <xliff:g id="number" example="29">%2$d</xliff:g></string>
<!-- Title for default enabled app compat changes category [CHAR LIMIT=50] -->
<string name="platform_compat_default_enabled_title">Default enabled changes</string>
<!-- Title for default disabled app compat changes category [CHAR LIMIT=50] -->
<string name="platform_compat_default_disabled_title">Default disabled changes</string>
- <!-- Title for target SDK gated app compat changes category [CHAR LIMIT=50] -->
- <string name="platform_compat_target_sdk_title">Enabled after SDK <xliff:g id="number" example="29">%d</xliff:g></string>
+ <!-- Title for target SDK gated app compat changes category (do not translate 'targetSdkVersion') [CHAR LIMIT=50] -->
+ <string name="platform_compat_target_sdk_title">Enabled for targetSdkVersion > <xliff:g id="number" example="29">%d</xliff:g></string>
<!-- Slices Strings -->
@@ -11301,14 +11328,6 @@
<!-- Message for Network connection connecting progress Dialog. Try to connect to wifi ap.[CHAR LIMIT=40] -->
<string name="network_connection_connecting_message">Connecting to device\u2026</string>
- <!-- Summary for bluetooth devices count in Bluetooth devices slice. [CHAR LIMIT=NONE] -->
- <plurals name="show_bluetooth_devices">
- <item quantity="one"><xliff:g id="number_device_count">%1$d</xliff:g> device connected</item>
- <item quantity="other"><xliff:g id="number_device_count">%1$d</xliff:g> devices connected</item>
- </plurals>
- <!-- Title for no bluetooth devices in Bluetooth devices slice. [CHAR LIMIT=NONE] -->
- <string name="no_bluetooth_devices">No Bluetooth devices</string>
-
<!-- Title for left bluetooth device. [CHAR LIMIT=NONE] -->
<string name="bluetooth_left_name">Left</string>
<!-- Title for right bluetooth device. [CHAR LIMIT=NONE] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 05191b8..a4c82b2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -462,26 +462,6 @@
<item name="rowStyle">@style/SliceRowStyle.Settings</item>
</style>
- <style name="TextAppearance.DeferredSetupCardTitle">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:textSize">16sp</item>
- </style>
-
- <style name="TextAppearance.DeferredSetupCardSummary"
- parent="@*android:style/TextAppearance.DeviceDefault.Body1">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
- </style>
-
- <style name="DeferredSetupCardButton" parent="android:Widget.DeviceDefault.Button.Colored">
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
- <item name="android:paddingBottom">@dimen/contextual_deferred_setup_card_button_padding_bottom</item>
- <item name="android:paddingEnd">@dimen/contextual_deferred_setup_card_button_padding_end</item>
- <item name="android:paddingStart">@dimen/contextual_deferred_setup_card_button_padding_start</item>
- <item name="android:paddingTop">@dimen/contextual_deferred_setup_card_button_padding_top</item>
- <item name="android:textAllCaps">false</item>
- <item name="android:textSize">14sp</item>
- </style>
-
<style name="SliceViewStyle">
<item name="rowStyle">@style/SliceRowStyle</item>
<item name="android:background">?android:attr/colorBackgroundFloating</item>
diff --git a/res/xml/about_legal.xml b/res/xml/about_legal.xml
index 90b1985..6c59cac 100644
--- a/res/xml/about_legal.xml
+++ b/res/xml/about_legal.xml
@@ -59,6 +59,7 @@
android:key="wallpaper_attributions"
android:title="@string/wallpaper_attributions"
android:summary="@string/wallpaper_attributions_values"
+ android:selectable="false"
settings:controller="com.android.settings.deviceinfo.legal.WallpaperAttributionsPreferenceController" />
</PreferenceScreen>
diff --git a/res/xml/accessibility_color_inversion_settings.xml b/res/xml/accessibility_color_inversion_settings.xml
index de1511f..9e31ff9 100644
--- a/res/xml/accessibility_color_inversion_settings.xml
+++ b/res/xml/accessibility_color_inversion_settings.xml
@@ -32,11 +32,17 @@
settings:allowDividerBelow="true"
settings:searchable="false" />
- <com.android.settingslib.widget.FooterPreference
- android:key="color_inversion_footer"
- android:persistent="false"
- android:selectable="false"
- android:title="@string/accessibility_display_inversion_preference_subtitle"
- settings:allowDividerAbove="true"
- settings:searchable="false" />
+ <PreferenceCategory
+ android:key="color_inversion_footer_category">
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="color_inversion_footer"
+ android:persistent="false"
+ android:selectable="false"
+ settings:allowDividerAbove="false"
+ android:title="@string/accessibility_display_inversion_preference_subtitle"
+ settings:searchable="false" />
+
+ </PreferenceCategory>
+
</PreferenceScreen>
diff --git a/res/xml/accessibility_daltonizer_settings.xml b/res/xml/accessibility_daltonizer_settings.xml
index 75386f5..976f912 100644
--- a/res/xml/accessibility_daltonizer_settings.xml
+++ b/res/xml/accessibility_daltonizer_settings.xml
@@ -26,31 +26,43 @@
android:persistent="false"
android:selectable="false"
android:title="@string/summary_placeholder"
+ settings:allowDividerBelow="true"
settings:searchable="false"/>
- <com.android.settingslib.widget.RadioButtonPreference
- android:key="daltonizer_mode_deuteranomaly"
- android:persistent="false"
- android:summary="@string/daltonizer_mode_deuteranomaly_summary"
- android:title="@string/daltonizer_mode_deuteranomaly_title"
- settings:allowDividerAbove="true" />
+ <PreferenceCategory
+ android:title="@string/daltonizer_type"
+ android:key="daltonizer_mode_category" >
- <com.android.settingslib.widget.RadioButtonPreference
- android:key="daltonizer_mode_protanomaly"
- android:persistent="false"
- android:summary="@string/daltonizer_mode_protanomaly_summary"
- android:title="@string/daltonizer_mode_protanomaly_title" />
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="daltonizer_mode_deuteranomaly"
+ android:persistent="false"
+ android:summary="@string/daltonizer_mode_deuteranomaly_summary"
+ android:title="@string/daltonizer_mode_deuteranomaly_title" />
- <com.android.settingslib.widget.RadioButtonPreference
- android:key="daltonizer_mode_tritanomaly"
- android:persistent="false"
- android:summary="@string/daltonizer_mode_tritanomaly_summary"
- android:title="@string/daltonizer_mode_tritanomaly_title" />
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="daltonizer_mode_protanomaly"
+ android:persistent="false"
+ android:summary="@string/daltonizer_mode_protanomaly_summary"
+ android:title="@string/daltonizer_mode_protanomaly_title" />
- <com.android.settingslib.widget.FooterPreference
- android:key="daltonizer_footer"
- android:persistent="false"
- android:selectable="false"
- android:title="@string/accessibility_display_daltonizer_preference_subtitle"
- settings:searchable="false" />
+ <com.android.settingslib.widget.RadioButtonPreference
+ android:key="daltonizer_mode_tritanomaly"
+ android:persistent="false"
+ android:summary="@string/daltonizer_mode_tritanomaly_summary"
+ android:title="@string/daltonizer_mode_tritanomaly_title" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:key="daltonizer_footer_category" >
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="daltonizer_footer"
+ android:persistent="false"
+ android:selectable="false"
+ settings:allowDividerAbove="false"
+ android:title="@string/accessibility_display_daltonizer_preference_subtitle"
+ settings:searchable="false" />
+
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/res/xml/dark_mode_settings.xml b/res/xml/dark_mode_settings.xml
index 9247a0c..cf91186 100644
--- a/res/xml/dark_mode_settings.xml
+++ b/res/xml/dark_mode_settings.xml
@@ -30,6 +30,16 @@
settings:controller="com.android.settings.display.darkmode.DarkModeScheduleSelectorController"
settings:keywords="@string/keywords_dark_ui_mode"/>
+ <Preference
+ android:key="dark_theme_start_time"
+ android:title="@string/night_display_start_time_title"
+ settings:searchable="false"/>
+
+ <Preference
+ android:key="dark_theme_end_time"
+ android:title="@string/night_display_end_time_title"
+ settings:searchable="false"/>
+
<com.android.settingslib.widget.LayoutPreference
android:key="dark_ui_activated"
android:title="@string/dark_ui_title"
diff --git a/res/xml/installed_app_launch_settings.xml b/res/xml/installed_app_launch_settings.xml
index a2a0ca4..6cf1955 100644
--- a/res/xml/installed_app_launch_settings.xml
+++ b/res/xml/installed_app_launch_settings.xml
@@ -21,16 +21,16 @@
<PreferenceCategory android:key="app_launch_domain_links"
android:title="@string/app_launch_domain_links_title">
- <DropDownPreference
- android:key="app_link_state"
- android:summary="%s"
- android:title="@string/app_launch_open_domain_urls_title" />
+ <Preference
+ android:key="app_link_state"
+ android:summary="@string/summary_placeholder"
+ android:title="@string/app_launch_open_domain_urls_title"/>
<com.android.settings.applications.AppDomainsPreference
- android:key="app_launch_supported_domain_urls"
- android:title="@string/app_launch_supported_domain_urls_title"
- android:dependency="app_link_state"
- />
+ android:key="app_launch_supported_domain_urls"
+ android:title="@string/app_launch_supported_domain_urls_title"
+ android:dependency="app_link_state"
+ />
</PreferenceCategory>
@@ -38,7 +38,7 @@
android:title="@string/app_launch_other_defaults_title">
<com.android.settings.applications.ClearDefaultsPreference
- android:key="app_launch_clear_defaults" />
+ android:key="app_launch_clear_defaults"/>
</PreferenceCategory>
diff --git a/res/xml/open_supported_links.xml b/res/xml/open_supported_links.xml
new file mode 100644
index 0000000..0f6e2ca
--- /dev/null
+++ b/res/xml/open_supported_links.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:title="@string/app_launch_open_domain_urls_title">
+
+ <com.android.settingslib.widget.LayoutPreference
+ android:key="app_header_view"
+ android:layout="@layout/settings_entity_header"
+ android:selectable="false"
+ settings:controller="com.android.settings.applications.AppHeaderPreferenceController"
+ settings:allowDividerBelow="true"/>
+
+ <PreferenceCategory
+ android:key="app_launch_allow_app_to_open_supported_links"
+ android:title="@string/app_link_open_always"
+ settings:controller="com.android.settings.applications.AppOpenSupportedLinksPreferenceController">
+ </PreferenceCategory>
+
+ <com.android.settingslib.widget.FooterPreference
+ android:key="supported_links_footer"
+ android:title="@string/open_supported_links_footer"
+ android:selectable="false"
+ settings:searchable="false"/>
+
+</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/SettingsDumpService.java b/src/com/android/settings/SettingsDumpService.java
index 487ccf5..2b6c7d8 100644
--- a/src/com/android/settings/SettingsDumpService.java
+++ b/src/com/android/settings/SettingsDumpService.java
@@ -102,11 +102,11 @@
JSONObject obj = new JSONObject();
DataUsageController controller = new DataUsageController(this);
ConnectivityManager connectivityManager = getSystemService(ConnectivityManager.class);
- SubscriptionManager manager = SubscriptionManager.from(this);
+ SubscriptionManager manager = this.getSystemService(SubscriptionManager.class);
TelephonyManager telephonyManager = this.getSystemService(TelephonyManager.class);
if (connectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
JSONArray array = new JSONArray();
- for (SubscriptionInfo info : manager.getAllSubscriptionInfoList()) {
+ for (SubscriptionInfo info : manager.getAvailableSubscriptionInfoList()) {
telephonyManager = telephonyManager
.createForSubscriptionId(info.getSubscriptionId());
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
diff --git a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
index 7136172..c96013f 100644
--- a/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityEditDialogUtils.java
@@ -171,7 +171,7 @@
private static void initMagnifyFullScreen(Context context, View view) {
final View dialogView = view.findViewById(R.id.magnify_full_screen);
- final String title = context.getString(
+ final CharSequence title = context.getText(
R.string.accessibility_magnification_area_settings_full_screen);
// TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
setupShortcutWidget(dialogView, title, R.drawable.accessibility_magnification_full_screen);
@@ -179,7 +179,7 @@
private static void initMagnifyWindowScreen(Context context, View view) {
final View dialogView = view.findViewById(R.id.magnify_window_screen);
- final String title = context.getString(
+ final CharSequence title = context.getText(
R.string.accessibility_magnification_area_settings_window_screen);
// TODO(b/146019459): Use vector drawable instead of temporal png file to avoid distorted.
setupShortcutWidget(dialogView, title,
@@ -214,9 +214,9 @@
private static void initHardwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.hardware_shortcut);
- final String title = context.getString(
+ final CharSequence title = context.getText(
R.string.accessibility_shortcut_edit_dialog_title_hardware);
- final String summary = context.getString(
+ final CharSequence summary = context.getText(
R.string.accessibility_shortcut_edit_dialog_summary_hardware);
setupShortcutWidget(dialogView, title, summary,
R.drawable.accessibility_shortcut_type_hardware);
@@ -225,9 +225,9 @@
private static void initMagnifyShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.triple_tap_shortcut);
- final String title = context.getString(
+ final CharSequence title = context.getText(
R.string.accessibility_shortcut_edit_dialog_title_triple_tap);
- final String summary = context.getString(
+ final CharSequence summary = context.getText(
R.string.accessibility_shortcut_edit_dialog_summary_triple_tap);
setupShortcutWidget(dialogView, title, summary,
R.drawable.accessibility_shortcut_type_triple_tap);
@@ -244,23 +244,34 @@
}
private static CharSequence retrieveTitle(Context context) {
- return context.getString(AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
- : R.string.accessibility_shortcut_edit_dialog_title_software);
+ int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+ }
+ return context.getText(resId);
}
private static CharSequence retrieveSummary(Context context, int lineHeight) {
- return AccessibilityUtil.isGestureNavigateEnabled(context)
- ? context.getString(
- R.string.accessibility_shortcut_edit_dialog_summary_software_gesture)
- : getSummaryStringWithIcon(context, lineHeight);
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ final int resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_summary_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_summary_software_gesture;
+ return context.getText(resId);
+ }
+ return getSummaryStringWithIcon(context, lineHeight);
}
private static int retrieveImageResId(Context context) {
- return AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.drawable.accessibility_shortcut_type_software_gesture
- : R.drawable.accessibility_shortcut_type_software;
// TODO(b/142531156): Use vector drawable instead of temporal png file to avoid distorted.
+ int resId = R.drawable.accessibility_shortcut_type_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
+ : R.drawable.accessibility_shortcut_type_software_gesture;
+ }
+ return resId;
}
private static SpannableString getSummaryStringWithIcon(Context context, int lineHeight) {
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index 5e6eced..0c8644f 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -27,7 +27,6 @@
import android.view.TextureView;
import android.view.View;
import android.view.Window;
-import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import androidx.annotation.ColorInt;
@@ -117,13 +116,13 @@
R.id.gesture_tutorial_video);
final TextView gestureTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
- VideoPlayer.create(context, isTouchExploreOn(context)
+ VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureTutorialVideo);
- gestureTutorialMessage.setText(isTouchExploreOn(context)
- ? R.string.accessibility_tutorial_dialog_message_gesture_with_talkback
- : R.string.accessibility_tutorial_dialog_message_gesture_without_talkback);
+ gestureTutorialMessage.setText(AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
+ : R.string.accessibility_tutorial_dialog_message_gesture);
break;
case DialogType.GESTURE_NAVIGATION_SETTINGS:
content = inflater.inflate(
@@ -132,14 +131,14 @@
R.id.gesture_tutorial_video);
final TextView gestureSettingsTutorialMessage = content.findViewById(
R.id.gesture_tutorial_message);
- VideoPlayer.create(context, isTouchExploreOn(context)
+ VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
? R.raw.illustration_accessibility_gesture_three_finger
: R.raw.illustration_accessibility_gesture_two_finger,
gestureSettingsTutorialVideo);
- gestureSettingsTutorialMessage.setText(isTouchExploreOn(context)
- ?
- R.string.accessibility_tutorial_dialog_message_gesture_settings_with_talkback
- : R.string.accessibility_tutorial_dialog_message_gesture_settings_without_talkback);
+ final int stringResId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
+ : R.string.accessibility_tutorial_dialog_message_gesture_settings;
+ gestureSettingsTutorialMessage.setText(stringResId);
break;
}
@@ -203,9 +202,4 @@
typedArray.recycle();
return colorResId;
}
-
- private static boolean isTouchExploreOn(Context context) {
- return ((AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))
- .isTouchExplorationEnabled();
- }
}
diff --git a/src/com/android/settings/accessibility/AccessibilityUtil.java b/src/com/android/settings/accessibility/AccessibilityUtil.java
index fefb406..8da6fbb 100644
--- a/src/com/android/settings/accessibility/AccessibilityUtil.java
+++ b/src/com/android/settings/accessibility/AccessibilityUtil.java
@@ -24,6 +24,7 @@
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.accessibility.AccessibilityManager;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
@@ -139,6 +140,12 @@
== NAV_BAR_MODE_GESTURAL;
}
+ /** Determines if a touch explore is being used. */
+ public static boolean isTouchExploreEnabled(Context context) {
+ final AccessibilityManager am = context.getSystemService(AccessibilityManager.class);
+ return am.isTouchExplorationEnabled();
+ }
+
/**
* Gets the corresponding fragment type of a given accessibility service.
*
diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
index daba21c..87c1bf8 100644
--- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java
@@ -35,12 +35,16 @@
import android.os.storage.StorageManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CheckBox;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
@@ -66,7 +70,7 @@
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
- private ShortcutPreference mShortcutPreference;
+ private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private int mUserShortcutType = UserShortcutType.DEFAULT;
// Used to restore the edit dialog status.
private int mUserShortcutTypeCache = UserShortcutType.DEFAULT;
@@ -115,14 +119,25 @@
}
@Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+ removeDialog(DialogType.EDIT_SHORTCUT);
+ mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+ };
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @Override
public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
// Restore the user shortcut type.
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
UserShortcutType.DEFAULT);
}
initShortcutPreference();
+
+ super.onViewCreated(view, savedInstanceState);
}
@Override
@@ -134,7 +149,12 @@
@Override
public void onResume() {
super.onResume();
+
mSettingsContentObserver.register(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
updateSwitchBarToggleSwitch();
updateShortcutPreferenceData();
updateShortcutPreference();
@@ -143,6 +163,10 @@
@Override
public void onPause() {
mSettingsContentObserver.unregister(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
super.onPause();
}
@@ -284,10 +308,13 @@
private String getShortcutTypeSummary(Context context) {
final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
- final CharSequence softwareTitle =
- context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
- : R.string.accessibility_shortcut_edit_dialog_title_software);
+ int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+ }
+ final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -372,6 +399,15 @@
}
}
+ @Override
+ protected void updateFooterTitle(PreferenceCategory category) {
+ final AccessibilityServiceInfo info = getAccessibilityServiceInfo();
+ final String titleText = (info == null) ? "" :
+ getString(R.string.accessibility_footer_title,
+ info.getResolveInfo().loadLabel(getPackageManager()));
+ category.setTitle(titleText);
+ }
+
private void initShortcutPreference() {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
mShortcutPreference = new ShortcutPreference(
@@ -380,9 +416,6 @@
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
mShortcutPreference.setOnClickListener(this);
- // Put the shortcutPreference before settingsPreference.
- mShortcutPreference.setOrder(-1);
- preferenceScreen.addPreference(mShortcutPreference);
}
private void updateShortcutPreference() {
diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
index ca32239..80fd92f 100644
--- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java
@@ -31,11 +31,14 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CheckBox;
import android.widget.Switch;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -58,11 +61,12 @@
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED;
private static final String PREVIEW_PREFERENCE_KEY = "color_inversion_preview";
+ private static final String CATEGORY_FOOTER_KEY = "color_inversion_footer_category";
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
private static final int DIALOG_ID_EDIT_SHORTCUT = 1;
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
private final Handler mHandler = new Handler();
- private ShortcutPreference mShortcutPreference;
+ private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private int mUserShortcutType = UserShortcutType.DEFAULT;
// Used to restore the edit dialog status.
@@ -71,18 +75,6 @@
private CheckBox mHardwareTypeCheckBox;
@Override
- public void onStart() {
- super.onStart();
- mSettingsContentObserver.register(getContentResolver());
- }
-
- @Override
- public void onStop() {
- mSettingsContentObserver.unregister(getContentResolver());
- super.onStop();
- }
-
- @Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_COLOR_INVERSION_SETTINGS;
}
@@ -105,10 +97,15 @@
@Override
protected void updateSwitchBarText(SwitchBar switchBar) {
- final String switchBarText = getString(
- R.string.accessibility_display_inversion_switch_title,
- getString(R.string.accessibility_display_inversion_switch_title));
- switchBar.setSwitchBarText(switchBarText, switchBarText);
+ switchBar.setSwitchBarText(R.string.accessibility_display_inversion_switch_title,
+ R.string.accessibility_display_inversion_switch_title);
+ }
+
+ @Override
+ protected void updateFooterTitle(PreferenceCategory category) {
+ final String titleText = getString(R.string.accessibility_footer_title,
+ getString(R.string.accessibility_display_inversion_preference_title));
+ category.setTitle(titleText);
}
@Override
@@ -127,13 +124,6 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- // Restore the user shortcut type.
- if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
- mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
- UserShortcutType.DEFAULT);
- }
- initShortcutPreference();
-
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@@ -144,10 +134,34 @@
== State.ON);
}
};
+
+ mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+ removeDialog(DIALOG_ID_EDIT_SHORTCUT);
+ mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+ };
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ // Restore the user shortcut type.
+ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
+ mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
+ UserShortcutType.DEFAULT);
+ }
+ initShortcutPreference();
+
+ super.onViewCreated(view, savedInstanceState);
+
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.setOrderingAsAdded(false);
+ final PreferenceCategory footerCategory = preferenceScreen.findPreference(
+ CATEGORY_FOOTER_KEY);
+ updateFooterTitle(footerCategory);
+ footerCategory.setOrder(Integer.MAX_VALUE);
+ }
+
+ @Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypeCache);
super.onSaveInstanceState(outState);
@@ -156,14 +170,30 @@
@Override
public void onResume() {
super.onResume();
+
+ mSettingsContentObserver.register(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
updateShortcutPreferenceData();
updateShortcutPreference();
}
@Override
+ public void onPause() {
+ mSettingsContentObserver.unregister(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
+ super.onPause();
+ }
+
+ @Override
public Dialog onCreateDialog(int dialogId) {
if (dialogId == DIALOG_ID_EDIT_SHORTCUT) {
- final CharSequence dialogTitle = getActivity().getString(
+ final CharSequence dialogTitle = getActivity().getText(
R.string.accessibility_shortcut_edit_dialog_title_daltonizer);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
getActivity(),
@@ -239,10 +269,13 @@
private String getShortcutTypeSummary(Context context) {
final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
- final CharSequence softwareTitle =
- context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
- : R.string.accessibility_shortcut_edit_dialog_title_software);
+ int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+ }
+ final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -318,9 +351,6 @@
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
mShortcutPreference.setOnClickListener(this);
- // Put the shortcutPreference before previewPreference.
- mShortcutPreference.setOrder(previewPreference.getOrder() - 1);
- preferenceScreen.addPreference(mShortcutPreference);
}
private void updateShortcutPreference() {
diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
index 3016f6a..ca95241 100644
--- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java
@@ -32,11 +32,14 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CheckBox;
import android.widget.Switch;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -47,7 +50,6 @@
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.RadioButtonPreference;
import java.util.ArrayList;
import java.util.HashSet;
@@ -61,13 +63,13 @@
SwitchBar.OnSwitchChangeListener, ShortcutPreference.OnClickListener {
private static final String ENABLED = Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED;
- private static final String PREFERENCE_KEY = "daltonizer_mode_deuteranomaly";
+ private static final String CATEGORY_FOOTER_KEY = "daltonizer_footer_category";
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
private static final int DIALOG_ID_EDIT_SHORTCUT = 1;
private static final List<AbstractPreferenceController> sControllers = new ArrayList<>();
private final Handler mHandler = new Handler();
- private ShortcutPreference mShortcutPreference;
+ private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private SettingsContentObserver mSettingsContentObserver;
private int mUserShortcutType = UserShortcutType.DEFAULT;
// Used to restore the edit dialog status.
@@ -100,13 +102,6 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- // Restore the user shortcut type.
- if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
- mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
- UserShortcutType.DEFAULT);
- }
- initShortcutPreference();
-
final List<String> enableServiceFeatureKeys = new ArrayList<>(/* initialCapacity= */ 1);
enableServiceFeatureKeys.add(ENABLED);
mSettingsContentObserver = new SettingsContentObserver(mHandler, enableServiceFeatureKeys) {
@@ -117,10 +112,34 @@
== State.ON);
}
};
+
+ mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+ removeDialog(DIALOG_ID_EDIT_SHORTCUT);
+ mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+ };
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ // Restore the user shortcut type.
+ if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
+ mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
+ UserShortcutType.DEFAULT);
+ }
+ initShortcutPreference();
+
+ super.onViewCreated(view, savedInstanceState);
+
+ final PreferenceScreen preferenceScreen = getPreferenceScreen();
+ preferenceScreen.setOrderingAsAdded(false);
+ final PreferenceCategory footerCategory = preferenceScreen.findPreference(
+ CATEGORY_FOOTER_KEY);
+ updateFooterTitle(footerCategory);
+ footerCategory.setOrder(Integer.MAX_VALUE);
+ }
+
+ @Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(EXTRA_SHORTCUT_TYPE, mUserShortcutTypeCache);
super.onSaveInstanceState(outState);
@@ -129,6 +148,12 @@
@Override
public void onResume() {
super.onResume();
+
+ mSettingsContentObserver.register(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(this);
@@ -141,17 +166,23 @@
@Override
public void onPause() {
- super.onPause();
+ mSettingsContentObserver.unregister(getContentResolver());
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
for (AbstractPreferenceController controller :
buildPreferenceControllers(getPrefContext(), getSettingsLifecycle())) {
((DaltonizerRadioButtonPreferenceController) controller).setOnChangeListener(null);
}
+
+ super.onPause();
}
@Override
public Dialog onCreateDialog(int dialogId) {
if (dialogId == DIALOG_ID_EDIT_SHORTCUT) {
- final CharSequence dialogTitle = getActivity().getString(
+ final CharSequence dialogTitle = getActivity().getText(
R.string.accessibility_shortcut_edit_dialog_title_daltonizer);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
getActivity(),
@@ -227,10 +258,13 @@
private String getShortcutTypeSummary(Context context) {
final int shortcutType = getUserShortcutType(context, UserShortcutType.SOFTWARE);
- final CharSequence softwareTitle =
- context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
- : R.string.accessibility_shortcut_edit_dialog_title_software);
+ int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+ }
+ final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
@@ -278,18 +312,6 @@
}
@Override
- public void onStart() {
- super.onStart();
- mSettingsContentObserver.register(getContentResolver());
- }
-
- @Override
- public void onStop() {
- mSettingsContentObserver.unregister(getContentResolver());
- super.onStop();
- }
-
- @Override
public int getMetricsCategory() {
return SettingsEnums.ACCESSIBILITY_TOGGLE_DALTONIZER;
}
@@ -325,9 +347,15 @@
@Override
protected void updateSwitchBarText(SwitchBar switchBar) {
- final String switchBarText = getString(R.string.accessibility_service_master_switch_title,
- getString(R.string.accessibility_display_daltonizer_preference_title));
- switchBar.setSwitchBarText(switchBarText, switchBarText);
+ switchBar.setSwitchBarText(R.string.accessibility_daltonizer_master_switch_title,
+ R.string.accessibility_daltonizer_master_switch_title);
+ }
+
+ @Override
+ protected void updateFooterTitle(PreferenceCategory category) {
+ final String titleText = getString(R.string.accessibility_footer_title,
+ getString(R.string.accessibility_display_daltonizer_preference_title));
+ category.setTitle(titleText);
}
@Override
@@ -370,10 +398,6 @@
mShortcutPreference.setTitle(R.string.accessibility_shortcut_title);
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
mShortcutPreference.setOnClickListener(this);
- final RadioButtonPreference radioButtonPreference = findPreference(PREFERENCE_KEY);
- // Put the shortcutPreference before radioButtonPreference.
- mShortcutPreference.setOrder(radioButtonPreference.getOrder() - 1);
- preferenceScreen.addPreference(mShortcutPreference);
}
private void updateShortcutPreferenceData() {
diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
index fe2449c..28c1e1c 100644
--- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java
@@ -29,6 +29,7 @@
import android.widget.ImageView;
import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
@@ -45,7 +46,8 @@
protected SwitchBar mSwitchBar;
protected ToggleSwitch mToggleSwitch;
-
+ protected ShortcutPreference mShortcutPreference;
+ protected Preference mSettingsPreference;
protected String mPreferenceKey;
protected CharSequence mSettingsTitle;
@@ -98,16 +100,6 @@
updateSwitchBarText(mSwitchBar);
PreferenceScreen preferenceScreen = getPreferenceScreen();
-
- // Show the "Settings" menu as if it were a preference screen
- if (mSettingsTitle != null && mSettingsIntent != null) {
- Preference settingsPref = new Preference(preferenceScreen.getContext());
- settingsPref.setTitle(mSettingsTitle);
- settingsPref.setIconSpaceReserved(true);
- settingsPref.setIntent(mSettingsIntent);
- preferenceScreen.addPreference(settingsPref);
- }
-
if (mImageUri != null) {
final AnimatedImagePreference animatedImagePreference = new AnimatedImagePreference(
preferenceScreen.getContext());
@@ -116,27 +108,55 @@
preferenceScreen.addPreference(animatedImagePreference);
}
- if (mStaticDescription != null) {
- final StaticTextPreference staticTextPreference = new StaticTextPreference(
- preferenceScreen.getContext());
- staticTextPreference.setSelectable(/* selectable= */ false);
- staticTextPreference.setSummary(mStaticDescription);
- preferenceScreen.addPreference(staticTextPreference);
+ // Show the "Settings" menu as if it were a preference screen.
+ if (mSettingsTitle != null && mSettingsIntent != null) {
+ mSettingsPreference = new Preference(preferenceScreen.getContext());
+ mSettingsPreference.setTitle(mSettingsTitle);
+ mSettingsPreference.setIconSpaceReserved(true);
+ mSettingsPreference.setIntent(mSettingsIntent);
}
- if (mHtmlDescription != null) {
- // For accessibility service, avoid malicious links made by third party developer
- final List<String> unsupportedTagList = new ArrayList<>();
- unsupportedTagList.add(ANCHOR_TAG);
+ if (mSettingsPreference != null || mShortcutPreference != null) {
+ final PreferenceCategory category = new PreferenceCategory(getPrefContext());
+ category.setTitle(R.string.accessibility_screen_option);
+ preferenceScreen.addPreference(category);
- final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(
- preferenceScreen.getContext());
- htmlTextPreference.setSelectable(/* selectable= */ false);
- htmlTextPreference.setSummary(mHtmlDescription);
- htmlTextPreference.setImageGetter(mImageGetter);
- htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
- htmlTextPreference.setDividerAllowedAbove(true);
- preferenceScreen.addPreference(htmlTextPreference);
+ if (mShortcutPreference != null) {
+ category.addPreference(mShortcutPreference);
+ }
+
+ if (mSettingsPreference != null) {
+ category.addPreference(mSettingsPreference);
+ }
+ }
+
+ if (mStaticDescription != null || mHtmlDescription != null) {
+ final PreferenceCategory footerCategory = new PreferenceCategory(getPrefContext());
+ updateFooterTitle(footerCategory);
+ preferenceScreen.addPreference(footerCategory);
+
+ if (mStaticDescription != null) {
+ final StaticTextPreference staticTextPreference = new StaticTextPreference(
+ preferenceScreen.getContext());
+ staticTextPreference.setSummary(mStaticDescription);
+ staticTextPreference.setSelectable(/* selectable= */ false);
+ footerCategory.addPreference(staticTextPreference);
+ }
+
+ if (mHtmlDescription != null) {
+ // For accessibility service, avoid malicious links made by third party developer.
+ final List<String> unsupportedTagList = new ArrayList<>();
+ unsupportedTagList.add(ANCHOR_TAG);
+
+ final HtmlTextPreference htmlTextPreference = new HtmlTextPreference(
+ preferenceScreen.getContext());
+ htmlTextPreference.setSummary(mHtmlDescription);
+ htmlTextPreference.setImageGetter(mImageGetter);
+ htmlTextPreference.setUnsupportedTagList(unsupportedTagList);
+ htmlTextPreference.setDividerAllowedAbove(true);
+ htmlTextPreference.setSelectable(/* selectable= */ false);
+ footerCategory.addPreference(htmlTextPreference);
+ }
}
}
@@ -149,16 +169,23 @@
@Override
public void onDestroyView() {
super.onDestroyView();
-
removeActionBarToggleSwitch();
}
protected void updateSwitchBarText(SwitchBar switchBar) {
- // Implement this to provide meaningful text in switch bar
+ // Implement this to provide meaningful text in switch bar.
switchBar.setSwitchBarText(R.string.accessibility_service_master_switch_title,
R.string.accessibility_service_master_switch_title);
}
+ protected void updateFooterTitle(PreferenceCategory category) {
+ // Implement this to provide meaningful text in the footer.
+ if (category != null) {
+ category.setTitle(getString(R.string.accessibility_footer_title,
+ mComponentName.getPackageName()));
+ }
+ }
+
protected abstract void onPreferenceToggled(String preferenceKey, boolean enabled);
protected void onInstallSwitchBarToggleSwitch() {
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 3060ddb..8e4da9d 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -37,6 +37,8 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RelativeLayout.LayoutParams;
@@ -67,7 +69,7 @@
private static final String SETTINGS_KEY = "screen_magnification_settings";
private static final String EXTRA_SHORTCUT_TYPE = "shortcut_type";
private static final String KEY_SHORTCUT_PREFERENCE = "shortcut_preference";
- private ShortcutPreference mShortcutPreference;
+ private TouchExplorationStateChangeListener mTouchExplorationStateChangeListener;
private int mUserShortcutType = UserShortcutType.DEFAULT;
// Used to restore the edit dialog status.
private int mUserShortcutTypeCache = UserShortcutType.DEFAULT;
@@ -84,6 +86,7 @@
protected Preference mConfigWarningPreference;
protected VideoPreference mVideoPreference;
+
protected class VideoPreference extends Preference {
private ImageView mVideoBackgroundView;
private OnGlobalLayoutListener mLayoutListener;
@@ -169,50 +172,44 @@
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
+ mTouchExplorationStateChangeListener = isTouchExplorationEnabled -> {
+ removeDialog(DialogType.EDIT_SHORTCUT);
+ mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
+ };
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ final PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen();
mVideoPreference = new VideoPreference(getPrefContext());
mVideoPreference.setSelectable(false);
mVideoPreference.setPersistent(false);
mVideoPreference.setLayoutResource(R.layout.magnification_video_preference);
+ preferenceScreen.addPreference(mVideoPreference);
- final PreferenceCategory optionCategory = new PreferenceCategory(getPrefContext());
- optionCategory.setTitle(R.string.accessibility_screen_option);
-
- // Restore the user shortcut type.
- if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_SHORTCUT_TYPE)) {
- mUserShortcutTypeCache = savedInstanceState.getInt(EXTRA_SHORTCUT_TYPE,
- UserShortcutType.DEFAULT);
- }
initShortcutPreference();
- final Preference settingsPreference = new Preference(getPrefContext());
- settingsPreference.setTitle(R.string.accessibility_magnification_service_settings_title);
- settingsPreference.setKey(SETTINGS_KEY);
- settingsPreference.setFragment(MagnificationSettingsFragment.class.getName());
- settingsPreference.setPersistent(false);
+ mSettingsPreference = new Preference(getPrefContext());
+ mSettingsPreference.setTitle(R.string.accessibility_magnification_service_settings_title);
+ mSettingsPreference.setKey(SETTINGS_KEY);
+ mSettingsPreference.setFragment(MagnificationSettingsFragment.class.getName());
+ mSettingsPreference.setPersistent(false);
- final PreferenceCategory aboutCategory = new PreferenceCategory(getPrefContext());
- aboutCategory.setTitle(R.string.accessibility_screen_magnification_about);
+ super.onViewCreated(view, savedInstanceState);
mConfigWarningPreference = new Preference(getPrefContext());
mConfigWarningPreference.setSelectable(false);
mConfigWarningPreference.setPersistent(false);
mConfigWarningPreference.setVisible(false);
- mConfigWarningPreference.setIcon(R.drawable.ic_warning_24dp);
+ preferenceScreen.addPreference(mConfigWarningPreference);
+ }
- final PreferenceScreen preferenceScreen = getPreferenceManager().getPreferenceScreen();
- preferenceScreen.setOrderingAsAdded(false);
- mVideoPreference.setOrder(0);
- optionCategory.setOrder(1);
- aboutCategory.setOrder(2);
- preferenceScreen.addPreference(mVideoPreference);
- preferenceScreen.addPreference(optionCategory);
- optionCategory.addPreference(mShortcutPreference);
- optionCategory.addPreference(settingsPreference);
- preferenceScreen.addPreference(aboutCategory);
- aboutCategory.addPreference(mConfigWarningPreference);
-
- return super.onCreateView(inflater, container, savedInstanceState);
+ @Override
+ protected void updateFooterTitle(PreferenceCategory category) {
+ final String titleText = getString(R.string.accessibility_footer_title,
+ getString(R.string.accessibility_screen_magnification_title));
+ category.setTitle(titleText);
}
@Override
@@ -225,6 +222,10 @@
public void onResume() {
super.onResume();
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.addTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
VideoView videoView = (VideoView) getView().findViewById(R.id.video);
if (videoView != null) {
videoView.start();
@@ -236,6 +237,15 @@
}
@Override
+ public void onPause() {
+ final AccessibilityManager am = getPrefContext().getSystemService(
+ AccessibilityManager.class);
+ am.removeTouchExplorationStateChangeListener(mTouchExplorationStateChangeListener);
+
+ super.onPause();
+ }
+
+ @Override
public Dialog onCreateDialog(int dialogId) {
switch (dialogId) {
case DialogType.GESTURE_NAVIGATION_TUTORIAL:
@@ -245,7 +255,7 @@
return AccessibilityGestureNavigationTutorial
.showAccessibilityButtonTutorialDialog(getActivity());
case DialogType.EDIT_SHORTCUT:
- final CharSequence dialogTitle = getActivity().getString(
+ final CharSequence dialogTitle = getActivity().getText(
R.string.accessibility_shortcut_edit_dialog_title_magnification);
final AlertDialog dialog =
AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
@@ -342,10 +352,13 @@
private String getShortcutTypeSummary(Context context) {
final int shortcutType = getUserShortcutType(context, UserShortcutType.DEFAULT);
- final CharSequence softwareTitle =
- context.getText(AccessibilityUtil.isGestureNavigateEnabled(context)
- ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture
- : R.string.accessibility_shortcut_edit_dialog_title_software);
+ int resId = R.string.accessibility_shortcut_edit_dialog_title_software;
+ if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
+ resId = AccessibilityUtil.isTouchExploreEnabled(context)
+ ? R.string.accessibility_shortcut_edit_dialog_title_software_gesture_talkback
+ : R.string.accessibility_shortcut_edit_dialog_title_software_gesture;
+ }
+ final CharSequence softwareTitle = context.getText(resId);
List<CharSequence> list = new ArrayList<>();
if ((shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
index 5fe62a7..ffa0c41 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentForSetupWizard.java
@@ -18,6 +18,7 @@
import android.app.settings.SettingsEnums;
import android.os.Bundle;
+import android.view.View;
public class ToggleScreenMagnificationPreferenceFragmentForSetupWizard
extends ToggleScreenMagnificationPreferenceFragment {
@@ -41,4 +42,12 @@
}
super.onStop();
}
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+
+ // Hide the setting from the vision settings.
+ mSettingsPreference.setVisible(false);
+ }
}
diff --git a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
index be9e8d4..5f01e4b 100644
--- a/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
+++ b/src/com/android/settings/accessibility/ToggleSelectToSpeakPreferenceFragmentForSetupWizard.java
@@ -20,7 +20,7 @@
import android.os.Bundle;
public class ToggleSelectToSpeakPreferenceFragmentForSetupWizard
- extends ToggleAccessibilityServicePreferenceFragment {
+ extends InvisibleToggleAccessibilityServicePreferenceFragment {
private boolean mToggleSwitchWasInitiallyChecked;
diff --git a/src/com/android/settings/applications/AppHeaderPreferenceController.java b/src/com/android/settings/applications/AppHeaderPreferenceController.java
new file mode 100644
index 0000000..8a77d6f
--- /dev/null
+++ b/src/com/android/settings/applications/AppHeaderPreferenceController.java
@@ -0,0 +1,107 @@
+/*
+ * 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.applications;
+
+import static com.android.settings.widget.EntityHeaderController.ActionType;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.IconDrawableFactory;
+
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settings.widget.EntityHeaderController;
+import com.android.settingslib.applications.AppUtils;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnResume;
+import com.android.settingslib.widget.LayoutPreference;
+
+/**
+ * The header controller displays on the top of the page.
+ */
+public class AppHeaderPreferenceController extends BasePreferenceController implements
+ LifecycleObserver, OnResume {
+ private DashboardFragment mParent;
+ private PackageInfo mPackageInfo;
+ private Lifecycle mLifecycle;
+ private LayoutPreference mHeaderPreference;
+
+ public AppHeaderPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ /**
+ * @param fragment set the parent fragment.
+ * @return return controller-self.
+ */
+ public AppHeaderPreferenceController setParentFragment(DashboardFragment fragment) {
+ mParent = fragment;
+ return this;
+ }
+
+ /**
+ * @param packageInfo set the {@link PackageInfo}.
+ * @return return controller-self.
+ */
+ public AppHeaderPreferenceController setPackageInfo(PackageInfo packageInfo) {
+ mPackageInfo = packageInfo;
+ return this;
+ }
+
+ /**
+ * @param lifeCycle set the {@link Lifecycle}.
+ * @return return controller-self.
+ */
+ public AppHeaderPreferenceController setLifeCycle(Lifecycle lifeCycle) {
+ mLifecycle = lifeCycle;
+ return this;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mHeaderPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void onResume() {
+ final Activity activity = mParent.getActivity();
+ final PackageManager packageManager = activity.getPackageManager();
+ EntityHeaderController
+ .newInstance(activity, mParent, mHeaderPreference.findViewById(R.id.entity_header))
+ .setRecyclerView(mParent.getListView(), mLifecycle)
+ .setIcon(IconDrawableFactory.newInstance(activity).getBadgedIcon(
+ mPackageInfo.applicationInfo))
+ .setLabel(mPackageInfo.applicationInfo.loadLabel(packageManager))
+ .setSummary(mPackageInfo)
+ .setIsInstantApp(AppUtils.isInstant(mPackageInfo.applicationInfo))
+ .setPackageName(mPackageInfo.packageName)
+ .setUid(mPackageInfo.applicationInfo.uid)
+ .setButtonActions(ActionType.ACTION_NONE, ActionType.ACTION_NONE)
+ .done(mParent.getActivity(), true /* rebindActions */);
+ }
+}
diff --git a/src/com/android/settings/applications/AppLaunchSettings.java b/src/com/android/settings/applications/AppLaunchSettings.java
index c61f5d1..17d8e67 100644
--- a/src/com/android/settings/applications/AppLaunchSettings.java
+++ b/src/com/android/settings/applications/AppLaunchSettings.java
@@ -17,14 +17,11 @@
package com.android.settings.applications;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
-import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import android.app.settings.SettingsEnums;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -36,24 +33,27 @@
import android.view.View.OnClickListener;
import androidx.appcompat.app.AlertDialog;
-import androidx.preference.DropDownPreference;
import androidx.preference.Preference;
-import androidx.preference.Preference.OnPreferenceChangeListener;
import com.android.settings.R;
import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
import java.util.List;
public class AppLaunchSettings extends AppInfoWithHeader implements OnClickListener,
Preference.OnPreferenceChangeListener {
private static final String TAG = "AppLaunchSettings";
-
private static final String KEY_APP_LINK_STATE = "app_link_state";
private static final String KEY_SUPPORTED_DOMAIN_URLS = "app_launch_supported_domain_urls";
private static final String KEY_CLEAR_DEFAULTS = "app_launch_clear_defaults";
+ private static final String FRAGMENT_OPEN_SUPPORTED_LINKS =
+ "com.android.settings.applications.OpenSupportedLinks";
+
+ public static final String KEY_PACKAGE_INFO = "pkg_info";
private static final Intent sBrowserIntent;
+
static {
sBrowserIntent = new Intent()
.setAction(Intent.ACTION_VIEW)
@@ -65,7 +65,7 @@
private boolean mIsBrowser;
private boolean mHasDomainUrls;
- private DropDownPreference mAppLinkState;
+ private Preference mAppLinkState;
private AppDomainsPreference mAppDomainUrls;
private ClearDefaultsPreference mClearDefaultsPreference;
@@ -76,7 +76,19 @@
addPreferencesFromResource(R.xml.installed_app_launch_settings);
mAppDomainUrls = (AppDomainsPreference) findPreference(KEY_SUPPORTED_DOMAIN_URLS);
mClearDefaultsPreference = (ClearDefaultsPreference) findPreference(KEY_CLEAR_DEFAULTS);
- mAppLinkState = (DropDownPreference) findPreference(KEY_APP_LINK_STATE);
+ mAppLinkState = findPreference(KEY_APP_LINK_STATE);
+ mAppLinkState.setOnPreferenceClickListener(preference -> {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_PACKAGE_INFO, this.mPackageInfo);
+
+ new SubSettingLauncher(this.getContext())
+ .setDestination(FRAGMENT_OPEN_SUPPORTED_LINKS)
+ .setArguments(args)
+ .setSourceMetricsCategory(SettingsEnums.APPLICATIONS_APP_LAUNCH)
+ .setTitleRes(-1)
+ .launch();
+ return true;
+ });
mPm = getActivity().getPackageManager();
@@ -85,13 +97,17 @@
(mAppEntry.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
if (!mIsBrowser) {
- List<IntentFilterVerificationInfo> iviList = mPm.getIntentFilterVerifications(mPackageName);
- List<IntentFilter> filters = mPm.getAllIntentFilters(mPackageName);
- CharSequence[] entries = getEntries(mPackageName, iviList, filters);
+ CharSequence[] entries = getEntries(mPackageName);
mAppDomainUrls.setTitles(entries);
mAppDomainUrls.setValues(new int[entries.length]);
+ mAppLinkState.setEnabled(mHasDomainUrls);
+ } else {
+ // Browsers don't show the app-link prefs
+ mAppLinkState.setShouldDisableView(true);
+ mAppLinkState.setEnabled(false);
+ mAppDomainUrls.setShouldDisableView(true);
+ mAppDomainUrls.setEnabled(false);
}
- buildStateDropDown();
}
// An app is a "browser" if it has an activity resolution that wound up
@@ -110,95 +126,35 @@
return false;
}
- private void buildStateDropDown() {
- if (mIsBrowser) {
- // Browsers don't show the app-link prefs
- mAppLinkState.setShouldDisableView(true);
- mAppLinkState.setEnabled(false);
- mAppDomainUrls.setShouldDisableView(true);
- mAppDomainUrls.setEnabled(false);
- } else {
- // Designed order of states in the dropdown:
- //
- // * always
- // * ask
- // * never
- //
- // Make sure to update linkStateToIndex() if this presentation order is changed.
- mAppLinkState.setEntries(new CharSequence[] {
- getString(R.string.app_link_open_always),
- getString(R.string.app_link_open_ask),
- getString(R.string.app_link_open_never),
- });
- mAppLinkState.setEntryValues(new CharSequence[] {
- Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS),
- Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK),
- Integer.toString(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER),
- });
-
- mAppLinkState.setEnabled(mHasDomainUrls);
- if (mHasDomainUrls) {
- // Present 'undefined' as 'ask' because the OS treats them identically for
- // purposes of the UI (and does the right thing around pending domain
- // verifications that might arrive after the user chooses 'ask' in this UI).
- final int state = mPm.getIntentVerificationStatusAsUser(mPackageName, UserHandle.myUserId());
- mAppLinkState.setValueIndex(linkStateToIndex(state));
-
- // Set the callback only after setting the initial selected item
- mAppLinkState.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- return updateAppLinkState(Integer.parseInt((String) newValue));
- }
- });
- }
- }
- }
-
- private int linkStateToIndex(final int state) {
+ private int linkStateToResourceId(int state) {
switch (state) {
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
- return 0; // Always
+ return R.string.app_link_open_always; // Always
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
- return 2; // Never
+ return R.string.app_link_open_never; // Never
default:
- return 1; // Ask
+ return R.string.app_link_open_ask; // Ask
}
}
- private boolean updateAppLinkState(final int newState) {
- if (mIsBrowser) {
- // We shouldn't get into this state, but if we do make sure
- // not to cause any permanent mayhem.
- return false;
- }
-
- final int userId = UserHandle.myUserId();
- final int priorState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
-
- if (priorState == newState) {
- return false;
- }
-
- boolean success = mPm.updateIntentVerificationStatusAsUser(mPackageName, newState, userId);
- if (success) {
- // Read back the state to see if the change worked
- final int updatedState = mPm.getIntentVerificationStatusAsUser(mPackageName, userId);
- success = (newState == updatedState);
- } else {
- Log.e(TAG, "Couldn't update intent verification status!");
- }
- return success;
- }
-
- private CharSequence[] getEntries(String packageName, List<IntentFilterVerificationInfo> iviList,
- List<IntentFilter> filters) {
+ private CharSequence[] getEntries(String packageName) {
ArraySet<String> result = Utils.getHandledDomains(mPm, packageName);
return result.toArray(new CharSequence[result.size()]);
}
+ private void setAppLinkStateSummary() {
+ final int state = mPm.getIntentVerificationStatusAsUser(mPackageName,
+ UserHandle.myUserId());
+ Log.d("[sunny]", "setAppLinkStateSummary+ state=" + state);
+ mAppLinkState.setSummary(linkStateToResourceId(state));
+ }
+
@Override
protected boolean refreshUi() {
+ if (mHasDomainUrls) {
+ //Update the summary after return from the OpenSupportedLinks
+ setAppLinkStateSummary();
+ }
mClearDefaultsPreference.setPackageName(mPackageName);
mClearDefaultsPreference.setAppEntry(mAppEntry);
return true;
diff --git a/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java b/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java
new file mode 100644
index 0000000..479d5dd
--- /dev/null
+++ b/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceController.java
@@ -0,0 +1,184 @@
+/*
+ * 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.applications;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.RadioButtonPreference;
+
+/**
+ * The radio group controller supports users to choose what kind supported links they need.
+ */
+public class AppOpenSupportedLinksPreferenceController extends BasePreferenceController
+ implements RadioButtonPreference.OnClickListener {
+ private static final String TAG = "OpenLinksPrefCtrl";
+ private static final String KEY_LINK_OPEN_ALWAYS = "app_link_open_always";
+ private static final String KEY_LINK_OPEN_ASK = "app_link_open_ask";
+ private static final String KEY_LINK_OPEN_NEVER = "app_link_open_never";
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private String mPackageName;
+ private int mCurrentIndex;
+ private PreferenceCategory mPreferenceCategory;
+ private String[] mRadioKeys = {KEY_LINK_OPEN_ALWAYS, KEY_LINK_OPEN_ASK, KEY_LINK_OPEN_NEVER};
+
+ @VisibleForTesting
+ RadioButtonPreference mAllowOpening;
+ @VisibleForTesting
+ RadioButtonPreference mAskEveryTime;
+ @VisibleForTesting
+ RadioButtonPreference mNotAllowed;
+
+ public AppOpenSupportedLinksPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+ }
+
+ /**
+ * @param pkg selected package name.
+ * @return return controller-self.
+ */
+ public AppOpenSupportedLinksPreferenceController setInit(String pkg) {
+ mPackageName = pkg;
+ return this;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreferenceCategory = screen.findPreference(getPreferenceKey());
+ mAllowOpening = makeRadioPreference(KEY_LINK_OPEN_ALWAYS, R.string.app_link_open_always);
+ final int entriesNo = getEntriesNo();
+ //This to avoid the summary line wrap
+ mAllowOpening.setAppendixVisibility(View.GONE);
+ mAllowOpening.setSummary(
+ mContext.getResources().getQuantityString(R.plurals.app_link_open_always_summary,
+ entriesNo, entriesNo));
+ mAskEveryTime = makeRadioPreference(KEY_LINK_OPEN_ASK, R.string.app_link_open_ask);
+ mNotAllowed = makeRadioPreference(KEY_LINK_OPEN_NEVER, R.string.app_link_open_never);
+
+ final int state = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+ UserHandle.myUserId());
+ mCurrentIndex = linkStateToIndex(state);
+ setRadioStatus(mCurrentIndex);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public void onRadioButtonClicked(RadioButtonPreference preference) {
+ final int clickedIndex = preferenceKeyToIndex(preference.getKey());
+ if (mCurrentIndex != clickedIndex) {
+ mCurrentIndex = clickedIndex;
+ setRadioStatus(mCurrentIndex);
+ updateAppLinkState(indexToLinkState(mCurrentIndex));
+ }
+ }
+
+ private RadioButtonPreference makeRadioPreference(String key, int resourceId) {
+ RadioButtonPreference pref = new RadioButtonPreference(mPreferenceCategory.getContext());
+ pref.setKey(key);
+ pref.setTitle(resourceId);
+ pref.setOnClickListener(this);
+ mPreferenceCategory.addPreference(pref);
+ return pref;
+ }
+
+ private int linkStateToIndex(int state) {
+ switch (state) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ return 0; // Always
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+ return 2; // Never
+ default:
+ return 1; // Ask
+ }
+ }
+
+ private int indexToLinkState(int index) {
+ switch (index) {
+ case 0:
+ return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ case 2:
+ return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+ default:
+ return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+ }
+ }
+
+ private int preferenceKeyToIndex(String key) {
+ for (int i = 0; i < mRadioKeys.length; i++) {
+ if (TextUtils.equals(key, mRadioKeys[i])) {
+ return i;
+ }
+ }
+ return 1; // Ask
+ }
+
+ private void setRadioStatus(int index) {
+ mAllowOpening.setChecked(index == 0 ? true : false);
+ mAskEveryTime.setChecked(index == 1 ? true : false);
+ mNotAllowed.setChecked(index == 2 ? true : false);
+ }
+
+ private boolean updateAppLinkState(final int newState) {
+ final int userId = UserHandle.myUserId();
+ final int priorState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+ userId);
+
+ if (priorState == newState) {
+ return false;
+ }
+
+ boolean success = mPackageManager.updateIntentVerificationStatusAsUser(mPackageName,
+ newState, userId);
+ if (success) {
+ // Read back the state to see if the change worked
+ final int updatedState = mPackageManager.getIntentVerificationStatusAsUser(mPackageName,
+ userId);
+ success = (newState == updatedState);
+ } else {
+ Log.e(TAG, "Couldn't update intent verification status!");
+ }
+ return success;
+ }
+
+ @VisibleForTesting
+ int getEntriesNo() {
+ return Utils.getHandledDomains(mPackageManager, mPackageName).size();
+ }
+}
diff --git a/src/com/android/settings/applications/OpenSupportedLinks.java b/src/com/android/settings/applications/OpenSupportedLinks.java
new file mode 100644
index 0000000..0e1531f
--- /dev/null
+++ b/src/com/android/settings/applications/OpenSupportedLinks.java
@@ -0,0 +1,97 @@
+/*
+ * 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.applications;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.Utils;
+import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.widget.FooterPreference;
+
+/**
+ * Display the Open Supported Links page. Allow users choose what kind supported links they need.
+ */
+public class OpenSupportedLinks extends DashboardFragment {
+ private static final String TAG = "OpenSupportedLinks";
+ private static final String FOOTER_KEY = "supported_links_footer";
+
+ @VisibleForTesting
+ PackageInfo mPackageInfo;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ final Bundle args = getArguments();
+ mPackageInfo = (args != null) ? args.getParcelable(AppLaunchSettings.KEY_PACKAGE_INFO)
+ : null;
+ if (mPackageInfo == null) {
+ Log.w(TAG, "Missing PackageInfo; maybe reinstalling?");
+ return;
+ }
+ use(AppHeaderPreferenceController.class).setParentFragment(this).setPackageInfo(
+ mPackageInfo).setLifeCycle(getSettingsLifecycle());
+ use(AppOpenSupportedLinksPreferenceController.class).setInit(mPackageInfo.packageName);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ super.onCreatePreferences(savedInstanceState, rootKey);
+ final FooterPreference footer = findPreference(FOOTER_KEY);
+ if (footer == null) {
+ Log.w(TAG, "Can't find the footer preference.");
+ return;
+ }
+ addLinksToFooter(footer);
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.open_supported_links;
+ }
+
+ @Override
+ protected String getLogTag() {
+ return TAG;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ return SettingsEnums.OPEN_SUPPORTED_LINKS;
+ }
+
+ @VisibleForTesting
+ void addLinksToFooter(FooterPreference footer) {
+ final ArraySet<String> result = Utils.getHandledDomains(getPackageManager(),
+ mPackageInfo.packageName);
+ if (result.isEmpty()) {
+ Log.w(TAG, "Can't find any app links.");
+ return;
+ }
+ CharSequence title = footer.getTitle() + System.lineSeparator();
+ for (String link : result) {
+ title = title + System.lineSeparator() + link;
+ }
+ footer.setTitle(title);
+ }
+}
diff --git a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
index a26e359..92b8a9d 100644
--- a/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
+++ b/src/com/android/settings/datausage/DataUsageSummaryPreferenceController.java
@@ -272,7 +272,7 @@
SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(mSubscriptionId);
if (subInfo == null) {
- subInfo = mSubscriptionManager.getAllSubscriptionInfoList().stream().filter(
+ subInfo = mSubscriptionManager.getAvailableSubscriptionInfoList().stream().filter(
i -> i.getSubscriptionId() == mSubscriptionId).findFirst().orElse(null);
}
if (subInfo != null && mHasMobileData) {
diff --git a/src/com/android/settings/datausage/DataUsageUtils.java b/src/com/android/settings/datausage/DataUsageUtils.java
index 0308cf7..5ed62fb 100644
--- a/src/com/android/settings/datausage/DataUsageUtils.java
+++ b/src/com/android/settings/datausage/DataUsageUtils.java
@@ -166,7 +166,7 @@
SubscriptionInfo subscriptionInfo =
subManager.getActiveSubscriptionInfo(subManager.getDefaultDataSubscriptionId());
if (subscriptionInfo == null) {
- List<SubscriptionInfo> list = subManager.getAllSubscriptionInfoList();
+ List<SubscriptionInfo> list = subManager.getAvailableSubscriptionInfoList();
if (list.size() == 0) {
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
diff --git a/src/com/android/settings/development/compat/PlatformCompatDashboard.java b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
index db90500..7b79da7 100644
--- a/src/com/android/settings/development/compat/PlatformCompatDashboard.java
+++ b/src/com/android/settings/development/compat/PlatformCompatDashboard.java
@@ -223,9 +223,8 @@
final Drawable icon = applicationInfo.loadIcon(context.getPackageManager());
final Preference appPreference = new Preference(context);
appPreference.setIcon(icon);
- appPreference.setSummary(mSelectedApp
- + " SDK "
- + applicationInfo.targetSdkVersion);
+ appPreference.setSummary(getString(R.string.platform_compat_selected_app_summary,
+ mSelectedApp, applicationInfo.targetSdkVersion));
appPreference.setKey(mSelectedApp);
appPreference.setOnPreferenceClickListener(
preference -> {
diff --git a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
index 8096c0b..6abd52d 100644
--- a/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
+++ b/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogController.java
@@ -567,8 +567,8 @@
return "TD_SCDMA";
case TelephonyManager.NETWORK_TYPE_IWLAN:
return "IWLAN";
- case TelephonyManager.NETWORK_TYPE_LTE_CA:
- return "LTE_CA";
+// case TelephonyManager.NETWORK_TYPE_LTE_CA:
+// return "LTE_CA";
case TelephonyManager.NETWORK_TYPE_NR:
return "NR";
default:
diff --git a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
index 23f625f..0175b95 100644
--- a/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
+++ b/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceController.java
@@ -1,8 +1,9 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
+ * 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
*
@@ -26,6 +27,8 @@
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.LayoutPreference;
+import java.time.LocalTime;
+
/**
* Controller for activate/deactivate night mode button
*/
@@ -34,12 +37,20 @@
private PowerManager mPowerManager;
private Button mTurnOffButton;
private Button mTurnOnButton;
+ private TimeFormatter mFormat;
public DarkModeActivationPreferenceController(Context context,
String preferenceKey) {
super(context, preferenceKey);
mPowerManager = context.getSystemService(PowerManager.class);
mUiModeManager = context.getSystemService(UiModeManager.class);
+ mFormat = new TimeFormatter(context);
+ }
+
+ public DarkModeActivationPreferenceController(Context context,
+ String preferenceKey, TimeFormatter f) {
+ this(context, preferenceKey);
+ mFormat = f;
}
@Override
@@ -58,13 +69,21 @@
}
private void updateNightMode(boolean active) {
- final int autoMode = mUiModeManager.getNightMode();
+ final int mode = mUiModeManager.getNightMode();
String buttonText;
- if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+ if (mode == UiModeManager.MODE_NIGHT_AUTO) {
buttonText = mContext.getString(active
? R.string.dark_ui_activation_off_auto
: R.string.dark_ui_activation_on_auto);
+ } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+ final LocalTime time = active
+ ? mUiModeManager.getCustomNightModeStart()
+ : mUiModeManager.getCustomNightModeEnd();
+ final String timeStr = mFormat.of(time);
+ buttonText = mContext.getString(active
+ ? R.string.dark_ui_activation_off_custom
+ : R.string.dark_ui_activation_on_custom, timeStr);
} else {
buttonText = mContext.getString(active
? R.string.dark_ui_activation_off_manual
@@ -85,11 +104,20 @@
public CharSequence getSummary() {
final boolean isActivated = (mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_YES) != 0;
- final int autoMode = mUiModeManager.getNightMode();
- if (autoMode == UiModeManager.MODE_NIGHT_AUTO) {
+ final int mode = mUiModeManager.getNightMode();
+ if (mode == UiModeManager.MODE_NIGHT_AUTO) {
return mContext.getString(isActivated
? R.string.dark_ui_summary_on_auto_mode_auto
: R.string.dark_ui_summary_off_auto_mode_auto);
+ } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+ final LocalTime time = isActivated
+ ? mUiModeManager.getCustomNightModeEnd()
+ : mUiModeManager.getCustomNightModeStart();
+ final String timeStr = mFormat.of(time);
+
+ return mContext.getString(isActivated
+ ? R.string.dark_ui_summary_on_auto_mode_custom
+ : R.string.dark_ui_summary_off_auto_mode_custom, timeStr);
} else {
return mContext.getString(isActivated
? R.string.dark_ui_summary_on_auto_mode_never
diff --git a/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java b/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java
new file mode 100644
index 0000000..5f22cb0
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceController.java
@@ -0,0 +1,105 @@
+/*
+ * 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.display.darkmode;
+
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.app.UiModeManager;
+import android.content.Context;
+import android.text.TextUtils;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.dashboard.DashboardFragment;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
+
+/**
+ * Controller for custom mode night mode time settings
+ */
+public class DarkModeCustomPreferenceController extends BasePreferenceController {
+ private static final String START_TIME_KEY = "dark_theme_start_time";
+ private static final String END_TIME_KEY = "dark_theme_end_time";
+ public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
+ private final UiModeManager mUiModeManager;
+ private TimeFormatter mFormat;
+ private DarkModeSettingsFragment mFragmet;
+
+ public DarkModeCustomPreferenceController(Context context, String key) {
+ super(context, key);
+ mFormat = new TimeFormatter(mContext);
+ mUiModeManager = context.getSystemService(UiModeManager.class);
+ }
+
+ public DarkModeCustomPreferenceController(
+ Context context, String key,
+ DarkModeSettingsFragment fragment) {
+ this(context, key);
+ mFragmet = fragment;
+ }
+
+ public DarkModeCustomPreferenceController(
+ Context context, String key,
+ DarkModeSettingsFragment fragment,
+ TimeFormatter format) {
+ this(context, key, fragment);
+ mFormat = format;
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ public TimePickerDialog getDialog() {
+ final LocalTime initialTime;
+ if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+ initialTime = mUiModeManager.getCustomNightModeStart();
+ } else {
+ initialTime = mUiModeManager.getCustomNightModeEnd();
+ }
+ return new TimePickerDialog(mContext, (view, hourOfDay, minute) -> {
+ final LocalTime time = LocalTime.of(hourOfDay, minute);
+ if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+ mUiModeManager.setCustomNightModeStart(time);
+ } else {
+ mUiModeManager.setCustomNightModeEnd(time);
+ }
+ if (mFragmet != null) {
+ mFragmet.refresh();
+ }
+ }, initialTime.getHour(), initialTime.getMinute(), mFormat.is24HourFormat());
+ }
+
+ @Override
+ protected void refreshSummary(Preference preference) {
+ if (mUiModeManager.getNightMode() != MODE_NIGHT_CUSTOM) {
+ preference.setVisible(false);
+ return;
+ }
+ preference.setVisible(true);
+ final LocalTime time;
+ if (TextUtils.equals(getPreferenceKey(), START_TIME_KEY)) {
+ time = mUiModeManager.getCustomNightModeStart();
+ } else {
+ time = mUiModeManager.getCustomNightModeEnd();
+ }
+ preference.setSummary(mFormat.of(time));
+ }
+}
diff --git a/src/com/android/settings/display/darkmode/DarkModePreference.java b/src/com/android/settings/display/darkmode/DarkModePreference.java
index c5fbded..7fb8023 100644
--- a/src/com/android/settings/display/darkmode/DarkModePreference.java
+++ b/src/com/android/settings/display/darkmode/DarkModePreference.java
@@ -22,6 +22,8 @@
import com.android.settings.R;
import com.android.settings.widget.MasterSwitchPreference;
+import java.time.LocalTime;
+
/**
* component for the display settings dark ui summary*/
public class DarkModePreference extends MasterSwitchPreference {
@@ -31,11 +33,14 @@
private PowerManager mPowerManager;
private Runnable mCallback;
+ private TimeFormatter mFormat;
+
public DarkModePreference(Context context, AttributeSet attrs) {
super(context, attrs);
mDarkModeObserver = new DarkModeObserver(context);
mUiModeManager = context.getSystemService(UiModeManager.class);
mPowerManager = context.getSystemService(PowerManager.class);
+ mFormat = new TimeFormatter(context);
mCallback = () -> {
final boolean batterySaver = mPowerManager.isPowerSaveMode();
final boolean active = (getContext().getResources().getConfiguration().uiMode
@@ -60,21 +65,30 @@
private void updateSummary(boolean batterySaver, boolean active) {
if (batterySaver) {
- final int stringId = active ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
+ final int stringId = active
+ ? R.string.dark_ui_mode_disabled_summary_dark_theme_on
: R.string.dark_ui_mode_disabled_summary_dark_theme_off;
setSummary(getContext().getString(stringId));
return;
}
- final boolean auto = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO;
-
+ final int mode = mUiModeManager.getNightMode();
String detail;
- if (active) {
- detail = getContext().getString(auto
+
+ if (mode == UiModeManager.MODE_NIGHT_AUTO) {
+ detail = getContext().getString(active
? R.string.dark_ui_summary_on_auto_mode_auto
- : R.string.dark_ui_summary_on_auto_mode_never);
+ : R.string.dark_ui_summary_off_auto_mode_auto);
+ } else if (mode == UiModeManager.MODE_NIGHT_CUSTOM) {
+ final LocalTime time = active
+ ? mUiModeManager.getCustomNightModeEnd()
+ : mUiModeManager.getCustomNightModeStart();
+ final String timeStr = mFormat.of(time);
+ detail = getContext().getString(active
+ ? R.string.dark_ui_summary_on_auto_mode_custom
+ : R.string.dark_ui_summary_off_auto_mode_custom, timeStr);
} else {
- detail = getContext().getString(auto
- ? R.string.dark_ui_summary_off_auto_mode_auto
+ detail = getContext().getString(active
+ ? R.string.dark_ui_summary_on_auto_mode_never
: R.string.dark_ui_summary_off_auto_mode_never);
}
String summary = getContext().getString(active
diff --git a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
index 9a86757..ea7fc03 100644
--- a/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
+++ b/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorController.java
@@ -63,8 +63,17 @@
}
private int getCurrentMode() {
- final int resId = mUiModeManager.getNightMode() == UiModeManager.MODE_NIGHT_AUTO
- ? R.string.dark_ui_auto_mode_auto : R.string.dark_ui_auto_mode_never;
+ int resId;
+ switch (mUiModeManager.getNightMode()) {
+ case UiModeManager.MODE_NIGHT_AUTO:
+ resId = R.string.dark_ui_auto_mode_auto;
+ break;
+ case UiModeManager.MODE_NIGHT_CUSTOM:
+ resId = R.string.dark_ui_auto_mode_custom;
+ break;
+ default:
+ resId = R.string.dark_ui_auto_mode_never;
+ }
return mPreference.findIndexOfValue(mContext.getString(resId));
}
@@ -85,6 +94,9 @@
} else if (mCurrentMode == mPreference.findIndexOfValue(
mContext.getString(R.string.dark_ui_auto_mode_auto))) {
mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_AUTO);
+ } else if (mCurrentMode == mPreference.findIndexOfValue(
+ mContext.getString(R.string.dark_ui_auto_mode_custom))) {
+ mUiModeManager.setNightMode(UiModeManager.MODE_NIGHT_CUSTOM);
}
return true;
}
diff --git a/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
index 6293316..97ee221 100644
--- a/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
+++ b/src/com/android/settings/display/darkmode/DarkModeSettingsFragment.java
@@ -14,14 +14,23 @@
package com.android.settings.display.darkmode;
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.app.UiModeManager;
import android.content.Context;
import android.os.Bundle;
import android.app.settings.SettingsEnums;
+import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Settings screen for Dark UI Mode
*/
@@ -29,15 +38,20 @@
public class DarkModeSettingsFragment extends DashboardFragment {
private static final String TAG = "DarkModeSettingsFragment";
+ private static final String DARK_THEME_END_TIME = "dark_theme_end_time";
+ private static final String DARK_THEME_START_TIME = "dark_theme_start_time";
private DarkModeObserver mContentObserver;
+ private DarkModeCustomPreferenceController mCustomStartController;
+ private DarkModeCustomPreferenceController mCustomEndController;
private Runnable mCallback = () -> {
updatePreferenceStates();
};
+ private static final int DIALOG_START_TIME = 0;
+ private static final int DIALOG_END_TIME = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
final Context context = getContext();
mContentObserver = new DarkModeObserver(context);
}
@@ -50,6 +64,18 @@
}
@Override
+ protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
+ List<AbstractPreferenceController> controllers = new ArrayList(2);
+ mCustomStartController = new DarkModeCustomPreferenceController(getContext(),
+ DARK_THEME_START_TIME, this);
+ mCustomEndController = new DarkModeCustomPreferenceController(getContext(),
+ DARK_THEME_END_TIME, this);
+ controllers.add(mCustomStartController);
+ controllers.add(mCustomEndController);
+ return controllers;
+ }
+
+ @Override
public void onStop() {
super.onStop();
// Stop listening for state changes.
@@ -57,6 +83,34 @@
}
@Override
+ public boolean onPreferenceTreeClick(Preference preference) {
+ if (DARK_THEME_END_TIME.equals(preference.getKey())) {
+ showDialog(DIALOG_END_TIME);
+ return true;
+ } else if (DARK_THEME_START_TIME.equals(preference.getKey())) {
+ showDialog(DIALOG_START_TIME);
+ return true;
+ }
+ return super.onPreferenceTreeClick(preference);
+ }
+
+ public void refresh() {
+ this.updatePreferenceStates();
+ }
+
+ @Override
+ public Dialog onCreateDialog(final int dialogId) {
+ if (dialogId == DIALOG_START_TIME || dialogId == DIALOG_END_TIME) {
+ if (dialogId == DIALOG_START_TIME) {
+ return mCustomStartController.getDialog();
+ } else {
+ return mCustomEndController.getDialog();
+ }
+ }
+ return super.onCreateDialog(dialogId);
+ }
+
+ @Override
protected int getPreferenceScreenResId() {
return R.xml.dark_mode_settings;
}
@@ -76,6 +130,18 @@
return SettingsEnums.DARK_UI_SETTINGS;
}
+ @Override
+ public int getDialogMetricsCategory(int dialogId) {
+ switch (dialogId) {
+ case DIALOG_START_TIME:
+ return SettingsEnums.DIALOG_DARK_THEME_SET_START_TIME;
+ case DIALOG_END_TIME:
+ return SettingsEnums.DIALOG_DARK_THEME_SET_END_TIME;
+ default:
+ return 0;
+ }
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.dark_mode_settings);
}
diff --git a/src/com/android/settings/display/darkmode/TimeFormatter.java b/src/com/android/settings/display/darkmode/TimeFormatter.java
new file mode 100644
index 0000000..87d22da
--- /dev/null
+++ b/src/com/android/settings/display/darkmode/TimeFormatter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.display.darkmode;
+
+import android.content.Context;
+
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+/**
+ * Formats LocalTime to the locale time string format
+*/
+public class TimeFormatter {
+ private final Context mContext;
+ public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
+ public TimeFormatter(Context context) {
+ mContext = context;
+ }
+
+ public String of(LocalTime time) {
+ return is24HourFormat() ? time.toString() : formatter.format(time);
+ }
+
+ public boolean is24HourFormat() {
+ return android.text.format.DateFormat.is24HourFormat(mContext);
+ }
+}
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
index e11ce30..4a02d91 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
@@ -80,10 +80,6 @@
LegacySuggestionContextualCardController.class,
LegacySuggestionContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.SLICE,
- SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP,
- SliceContextualCardController.class,
- SliceContextualCardRenderer.class));
- add(new ControllerRendererMapping(CardType.SLICE,
SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
SliceContextualCardController.class,
SliceContextualCardRenderer.class));
@@ -91,6 +87,10 @@
SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
SliceContextualCardController.class,
SliceContextualCardRenderer.class));
+ add(new ControllerRendererMapping(CardType.SLICE,
+ SliceContextualCardRenderer.VIEW_TYPE_STICKY,
+ SliceContextualCardController.class,
+ SliceContextualCardRenderer.class));
add(new ControllerRendererMapping(CardType.CONDITIONAL_FOOTER,
ConditionFooterContextualCardRenderer.VIEW_TYPE,
ConditionContextualCardController.class,
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index d02f103..9e9069f 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -17,13 +17,15 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardLoader.CARD_CONTENT_LOADER_ID;
-import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE;
import static com.android.settings.intelligence.ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE;
+import static com.android.settings.slices.CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI;
+import static com.android.settings.slices.CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI;
import static java.util.stream.Collectors.groupingBy;
import android.app.settings.SettingsEnums;
import android.content.Context;
+import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.text.format.DateUtils;
@@ -52,6 +54,7 @@
import com.android.settingslib.core.lifecycle.events.OnStop;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -81,6 +84,8 @@
static final String KEY_CONTEXTUAL_CARDS = "key_contextual_cards";
private static final String TAG = "ContextualCardManager";
+ private static final List<Uri> STICKY_CARDS =
+ Arrays.asList(CONTEXTUAL_WIFI_SLICE_URI, BLUETOOTH_DEVICES_SLICE_URI);
private final Context mContext;
private final Lifecycle mLifecycle;
@@ -310,7 +315,7 @@
return cards;
}
- final List<ContextualCard> result = getCardsWithDeferredSetupViewType(cards);
+ final List<ContextualCard> result = getCardsWithStickyViewType(cards);
return getCardsWithSuggestionViewType(result);
}
@@ -341,17 +346,24 @@
return result;
}
- private List<ContextualCard> getCardsWithDeferredSetupViewType(List<ContextualCard> cards) {
- // Find the deferred setup card and assign it with proper view type.
- // Reason: The returned card list will mix deferred setup card and other suggestion cards
- // after device running 1 days.
+ // TODO(b/143055685):use category to determine whether they are sticky.
+ private List<ContextualCard> getCardsWithStickyViewType(List<ContextualCard> cards) {
final List<ContextualCard> result = new ArrayList<>(cards);
+ int replaceCount = 0;
for (int index = 0; index < result.size(); index++) {
+ if (replaceCount > STICKY_CARDS.size() - 1) {
+ break;
+ }
+
final ContextualCard card = cards.get(index);
- if (card.getCategory() == DEFERRED_SETUP_VALUE) {
+ if (card.getCardType() != ContextualCard.CardType.SLICE) {
+ continue;
+ }
+
+ if (STICKY_CARDS.contains(card.getSliceUri())) {
result.set(index, card.mutate().setViewType(
- SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP).build());
- return result;
+ SliceContextualCardRenderer.VIEW_TYPE_STICKY).build());
+ replaceCount++;
}
}
return result;
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
index cf1e61f..3d75962 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSlice.java
@@ -16,6 +16,8 @@
package com.android.settings.homepage.contextualcards.slices;
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
import android.app.PendingIntent;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
@@ -38,6 +40,7 @@
import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
+import com.android.settings.bluetooth.BluetoothPairingDetail;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.slices.CustomSliceRegistry;
@@ -64,17 +67,18 @@
* than {@link #DEFAULT_EXPANDED_ROW_COUNT}.
*/
@VisibleForTesting
- static final int DEFAULT_EXPANDED_ROW_COUNT = 3;
+ static final int DEFAULT_EXPANDED_ROW_COUNT = 2;
/**
* Refer {@link com.android.settings.bluetooth.BluetoothDevicePreference#compareTo} to sort the
* Bluetooth devices by {@link CachedBluetoothDevice}.
*/
- private static final Comparator<CachedBluetoothDevice> COMPARATOR
- = Comparator.naturalOrder();
+ private static final Comparator<CachedBluetoothDevice> COMPARATOR = Comparator.naturalOrder();
private static final String TAG = "BluetoothDevicesSlice";
+ private static int sToggledState;
+
private final Context mContext;
public BluetoothDevicesSlice(Context context) {
@@ -88,43 +92,52 @@
@Override
public Slice getSlice() {
+ final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (btAdapter == null) {
+ Log.i(TAG, "Bluetooth is not supported on this hardware platform");
+ return null;
+ }
+
// Reload theme for switching dark mode on/off
mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);
final IconCompat icon = IconCompat.createWithResource(mContext,
com.android.internal.R.drawable.ic_settings_bluetooth);
- final CharSequence title = mContext.getText(R.string.bluetooth_devices);
- final CharSequence titleNoBluetoothDevices = mContext.getText(
- R.string.no_bluetooth_devices);
+ final CharSequence title = mContext.getText(R.string.bluetooth_settings_title);
final PendingIntent primaryActionIntent = PendingIntent.getActivity(mContext, 0,
getIntent(), 0);
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryActionIntent, icon,
ListBuilder.ICON_IMAGE, title);
- final ListBuilder listBuilder =
- new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
- .setAccentColor(COLOR_NOT_TINTED);
+ final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+ .setAccentColor(COLOR_NOT_TINTED)
+ .setHeader(new ListBuilder.HeaderBuilder()
+ .setTitle(title)
+ .setPrimaryAction(primarySliceAction));
+
+ // Only show a toggle when Bluetooth is off and not turning on.
+ if ((!isBluetoothEnabled(btAdapter) && sToggledState != BluetoothAdapter.STATE_TURNING_ON)
+ || sToggledState == BluetoothAdapter.STATE_TURNING_OFF) {
+ sToggledState = 0;
+ final PendingIntent toggleAction = getBroadcastIntent(mContext);
+ final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
+ null /* actionTitle */, false /* isChecked */);
+ return listBuilder
+ .addAction(toggleSliceAction)
+ .build();
+ }
+ sToggledState = 0;
// Get row builders by Bluetooth devices.
final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilder();
-
- // Return a header with IsError flag, if no Bluetooth devices.
if (rows.isEmpty()) {
- return listBuilder.setHeader(new ListBuilder.HeaderBuilder()
- .setTitle(titleNoBluetoothDevices)
- .setPrimaryAction(primarySliceAction))
- .setIsError(true)
+ return listBuilder
+ .addRow(getPairNewDeviceRow())
.build();
}
// Get displayable device count.
final int deviceCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);
- // According to the displayable device count to set sub title of header.
- listBuilder.setHeader(new ListBuilder.HeaderBuilder()
- .setTitle(title)
- .setSubtitle(getSubTitle(deviceCount))
- .setPrimaryAction(primarySliceAction));
-
// According to the displayable device count to add bluetooth device rows.
for (int i = 0; i < deviceCount; i++) {
listBuilder.addRow(rows.get(i));
@@ -148,6 +161,20 @@
@Override
public void onNotifyChange(Intent intent) {
+ final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ final boolean currentState = isBluetoothEnabled(btAdapter);
+ final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, currentState);
+ if (newState != currentState) {
+ if (newState) {
+ sToggledState = BluetoothAdapter.STATE_TURNING_ON;
+ btAdapter.enable();
+ } else {
+ sToggledState = BluetoothAdapter.STATE_TURNING_OFF;
+ btAdapter.disable();
+ }
+ mContext.getContentResolver().notifyChange(getUri(), null);
+ }
+
// Activate available media device.
final int bluetoothDeviceHashCode = intent.getIntExtra(BLUETOOTH_DEVICE_HASH_CODE, -1);
for (CachedBluetoothDevice cachedBluetoothDevice : getConnectedBluetoothDevices()) {
@@ -203,7 +230,7 @@
// The requestCode should be unique, use the hashcode of device as request code.
return PendingIntent
- .getActivity(mContext, device.hashCode() /* requestCode */,
+ .getActivity(mContext, device.hashCode() /* requestCode */,
subSettingLauncher.toIntent(),
0 /* flags */);
}
@@ -223,6 +250,23 @@
return Utils.createIconWithDrawable(drawable);
}
+ private ListBuilder.RowBuilder getPairNewDeviceRow() {
+ final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_add_24dp);
+ final String title = mContext.getString(R.string.bluetooth_pairing_pref_title);
+ final Intent intent = new SubSettingLauncher(mContext)
+ .setDestination(BluetoothPairingDetail.class.getName())
+ .setTitleRes(R.string.bluetooth_pairing_page_title)
+ .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_PAIRING)
+ .toIntent();
+ final PendingIntent pi = PendingIntent.getActivity(mContext, intent.hashCode(), intent,
+ 0 /* flags */);
+ final SliceAction action = SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE,
+ title);
+ return new ListBuilder.RowBuilder()
+ .setTitleItem(action)
+ .setTitle(title);
+ }
+
private List<ListBuilder.RowBuilder> getBluetoothRowBuilder() {
// According to Bluetooth devices to create row builders.
final List<ListBuilder.RowBuilder> bluetoothRows = new ArrayList<>();
@@ -272,8 +316,13 @@
bluetoothDevice.getName());
}
- private CharSequence getSubTitle(int deviceCount) {
- return mContext.getResources().getQuantityString(R.plurals.show_bluetooth_devices,
- deviceCount, deviceCount);
+ private boolean isBluetoothEnabled(BluetoothAdapter btAdapter) {
+ switch (btAdapter.getState()) {
+ case BluetoothAdapter.STATE_ON:
+ case BluetoothAdapter.STATE_TURNING_ON:
+ return true;
+ default:
+ return false;
+ }
}
}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
index 45be727..6f07d47 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRenderer.java
@@ -54,7 +54,7 @@
public class SliceContextualCardRenderer implements ContextualCardRenderer, LifecycleObserver {
public static final int VIEW_TYPE_FULL_WIDTH = R.layout.contextual_slice_full_tile;
public static final int VIEW_TYPE_HALF_WIDTH = R.layout.contextual_slice_half_tile;
- public static final int VIEW_TYPE_DEFERRED_SETUP = R.layout.contextual_slice_deferred_setup;
+ public static final int VIEW_TYPE_STICKY = R.layout.contextual_slice_sticky_tile;
private static final String TAG = "SliceCardRenderer";
@@ -66,7 +66,6 @@
private final Context mContext;
private final LifecycleOwner mLifecycleOwner;
private final ControllerRendererPool mControllerRendererPool;
- private final SliceDeferredSetupCardRendererHelper mDeferredSetupCardHelper;
private final SliceFullCardRendererHelper mFullCardHelper;
private final SliceHalfCardRendererHelper mHalfCardHelper;
@@ -80,14 +79,11 @@
mLifecycleOwner.getLifecycle().addObserver(this);
mFullCardHelper = new SliceFullCardRendererHelper(context);
mHalfCardHelper = new SliceHalfCardRendererHelper(context);
- mDeferredSetupCardHelper = new SliceDeferredSetupCardRendererHelper(context);
}
@Override
public RecyclerView.ViewHolder createViewHolder(View view, @LayoutRes int viewType) {
- if (viewType == VIEW_TYPE_DEFERRED_SETUP) {
- return mDeferredSetupCardHelper.createViewHolder(view);
- } else if (viewType == VIEW_TYPE_HALF_WIDTH) {
+ if (viewType == VIEW_TYPE_HALF_WIDTH) {
return mHalfCardHelper.createViewHolder(view);
}
return mFullCardHelper.createViewHolder(view);
@@ -132,9 +128,7 @@
return;
}
- if (holder.getItemViewType() == VIEW_TYPE_DEFERRED_SETUP) {
- mDeferredSetupCardHelper.bindView(holder, card, slice);
- } else if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
+ if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
mHalfCardHelper.bindView(holder, card, slice);
} else {
mFullCardHelper.bindView(holder, card, slice);
@@ -144,11 +138,7 @@
}
});
- if (holder.getItemViewType()
- == VIEW_TYPE_DEFERRED_SETUP) {// Deferred setup is never dismissible.
- } else if (holder.getItemViewType() == VIEW_TYPE_HALF_WIDTH) {
- initDismissalActions(holder, card);
- } else {
+ if (holder.getItemViewType() != VIEW_TYPE_STICKY) {
initDismissalActions(holder, card);
}
diff --git a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java b/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
deleted file mode 100644
index ea9e424..0000000
--- a/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelper.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.slices;
-
-import android.app.PendingIntent;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.Slice;
-import androidx.slice.SliceMetadata;
-import androidx.slice.core.SliceAction;
-import androidx.slice.widget.EventInfo;
-
-import com.android.settings.R;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settings.homepage.contextualcards.logging.ContextualCardLogUtils;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-
-/**
- * Card renderer helper for {@link ContextualCard} built as slice deferred setup card.
- */
-class SliceDeferredSetupCardRendererHelper {
- private static final String TAG = "SliceDSCRendererHelper";
-
- private final Context mContext;
-
- SliceDeferredSetupCardRendererHelper(Context context) {
- mContext = context;
- }
-
- RecyclerView.ViewHolder createViewHolder(View view) {
- return new DeferredSetupCardViewHolder(view);
- }
-
- void bindView(RecyclerView.ViewHolder holder, ContextualCard card, Slice slice) {
- final DeferredSetupCardViewHolder view = (DeferredSetupCardViewHolder) holder;
- final SliceMetadata sliceMetadata = SliceMetadata.from(mContext, slice);
- final SliceAction primaryAction = sliceMetadata.getPrimaryAction();
- view.icon.setImageDrawable(primaryAction.getIcon().loadDrawable(mContext));
- view.title.setText(primaryAction.getTitle());
- view.summary.setText(sliceMetadata.getSubtitle());
- view.button.setOnClickListener(v -> {
- try {
- primaryAction.getAction().send();
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "Failed to start intent " + primaryAction.getTitle());
- }
- final String log = ContextualCardLogUtils.buildCardClickLog(card, 0 /* row */,
- EventInfo.ACTION_TYPE_CONTENT, view.getAdapterPosition());
-
- final MetricsFeatureProvider metricsFeatureProvider =
- FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
-
- metricsFeatureProvider.action(mContext,
- SettingsEnums.ACTION_CONTEXTUAL_CARD_CLICK, log);
- });
- }
-
- static class DeferredSetupCardViewHolder extends RecyclerView.ViewHolder {
- public final LinearLayout content;
- public final ImageView icon;
- public final TextView title;
- public final TextView summary;
- public final Button button;
-
- public DeferredSetupCardViewHolder(View itemView) {
- super(itemView);
- content = itemView.findViewById(R.id.content);
- icon = itemView.findViewById(android.R.id.icon);
- title = itemView.findViewById(android.R.id.title);
- summary = itemView.findViewById(android.R.id.summary);
- button = itemView.findViewById(R.id.finish_setup);
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/network/OWNERS b/src/com/android/settings/network/OWNERS
index bedbe16..d23f816 100644
--- a/src/com/android/settings/network/OWNERS
+++ b/src/com/android/settings/network/OWNERS
@@ -1,6 +1,8 @@
# Default reviewers for this and subdirectories.
+allenwtsu@google.com
andychou@google.com
bonianchen@google.com
-allenwtsu@google.com
+songferngwang@google.com
+tomhsu@google.com
# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 41c7c14..9105db8 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -32,7 +32,9 @@
import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
public class SubscriptionUtil {
private static final String TAG = "SubscriptionUtil";
@@ -79,12 +81,7 @@
if (sAvailableResultsForTesting != null) {
return sAvailableResultsForTesting;
}
- final SubscriptionManager subMgr = context.getSystemService(SubscriptionManager.class);
-
- final List<SubscriptionInfo> subscriptions =
- new ArrayList<>(emptyIfNull(subMgr.getSelectableSubscriptionInfoList()));
-
- return subscriptions;
+ return new ArrayList<>(emptyIfNull(getSelectableSubscriptionInfoList(context)));
}
/**
@@ -242,4 +239,70 @@
}
return info.getSimSlotIndex();
}
+
+ /**
+ * Return a list of subscriptions that are available and visible to the user.
+ *
+ * @return list of user selectable subscriptions.
+ */
+ public static List<SubscriptionInfo> getSelectableSubscriptionInfoList(Context context) {
+ SubscriptionManager subManager = context.getSystemService(SubscriptionManager.class);
+ List<SubscriptionInfo> availableList = subManager.getAvailableSubscriptionInfoList();
+ if (availableList == null) {
+ return null;
+ } else {
+ // Multiple subscriptions in a group should only have one representative.
+ // It should be the current active primary subscription if any, or any
+ // primary subscription.
+ List<SubscriptionInfo> selectableList = new ArrayList<>();
+ Map<ParcelUuid, SubscriptionInfo> groupMap = new HashMap<>();
+
+ for (SubscriptionInfo info : availableList) {
+ // Opportunistic subscriptions are considered invisible
+ // to users so they should never be returned.
+ if (!isSubscriptionVisible(subManager, context, info)) continue;
+
+ ParcelUuid groupUuid = info.getGroupUuid();
+ if (groupUuid == null) {
+ // Doesn't belong to any group. Add in the list.
+ selectableList.add(info);
+ } else if (!groupMap.containsKey(groupUuid)
+ || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX
+ && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) {
+ // If it belongs to a group that has never been recorded or it's the current
+ // active subscription, add it in the list.
+ selectableList.remove(groupMap.get(groupUuid));
+ selectableList.add(info);
+ groupMap.put(groupUuid, info);
+ }
+
+ }
+ return selectableList;
+ }
+ }
+
+
+ /**
+ * Whether a subscription is visible to API caller. If it's a bundled opportunistic
+ * subscription, it should be hidden anywhere in Settings, dialer, status bar etc.
+ * Exception is if caller owns carrier privilege, in which case they will
+ * want to see their own hidden subscriptions.
+ *
+ * @param info the subscriptionInfo to check against.
+ * @return true if this subscription should be visible to the API caller.
+ */
+ private static boolean isSubscriptionVisible(
+ SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) {
+ if (info == null) return false;
+ // If subscription is NOT grouped opportunistic subscription, it's visible.
+ if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
+
+ // If the caller is the carrier app and owns the subscription, it should be visible
+ // to the caller.
+ TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(info.getSubscriptionId());
+ boolean hasCarrierPrivilegePermission = telephonyManager.hasCarrierPrivileges()
+ || subscriptionManager.canManageSubscription(info);
+ return hasCarrierPrivilegePermission;
+ }
}
diff --git a/src/com/android/settings/network/ims/ImsDirectQuery.java b/src/com/android/settings/network/ims/ImsDirectQuery.java
new file mode 100644
index 0000000..4c6a932
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsDirectQuery.java
@@ -0,0 +1,32 @@
+/*
+ * 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.network.ims;
+
+
+/**
+ * An interface for direct querying IMS, and return {@code boolean}
+ */
+public interface ImsDirectQuery {
+
+ /**
+ * Interface for performing IMS status/configuration query through public APIs
+ *
+ * @return result of query in boolean
+ */
+ boolean directQuery();
+
+}
diff --git a/src/com/android/settings/network/ims/ImsDirectQueryImpl.java b/src/com/android/settings/network/ims/ImsDirectQueryImpl.java
new file mode 100644
index 0000000..cff8461
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsDirectQueryImpl.java
@@ -0,0 +1,56 @@
+/*
+ * 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.network.ims;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+
+
+/**
+ * An implementation of {@code ImsQuery} and {@code ImsDirectQuery}.
+ */
+abstract class ImsDirectQueryImpl implements ImsQuery, ImsDirectQuery, Callable<Boolean> {
+
+ /**
+ * Implementation of interface {@code ImsQuery}
+ *
+ * @param executors {@code ExecutorService} which allows to submit {@code ImsQuery} when
+ * required
+ * @return result of query in format of {@code Future<Boolean>}
+ */
+ public Future<Boolean> query(ExecutorService executors) throws RejectedExecutionException {
+ return executors.submit(this);
+ }
+
+ /**
+ * Implementation of interface {@code ImsDirectQuery}
+ *
+ * @return result of query
+ */
+ public boolean directQuery() {
+ return call();
+ }
+
+ /**
+ * Query running within a {@code Callable}
+ *
+ * @return result of query
+ */
+ public abstract Boolean call();
+}
diff --git a/src/com/android/settings/network/ims/ImsQuery.java b/src/com/android/settings/network/ims/ImsQuery.java
new file mode 100644
index 0000000..1462718
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQuery.java
@@ -0,0 +1,38 @@
+/*
+ * 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.network.ims;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
+
+
+/**
+ * An interface for querying IMS, and return {@code Future<Boolean>}
+ */
+public interface ImsQuery {
+
+ /**
+ * Interface for performing IMS status/configuration query through ExecutorService
+ *
+ * @param executors {@code ExecutorService} which allows to submit {@code ImsQuery} when
+ * required
+ * @return result of query in format of {@code Future<Boolean>}
+ */
+ Future<Boolean> query(ExecutorService executors) throws RejectedExecutionException;
+
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryController.java b/src/com/android/settings/network/ims/ImsQueryController.java
new file mode 100644
index 0000000..651c422
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryController.java
@@ -0,0 +1,37 @@
+/*
+ * 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.network.ims;
+
+import android.content.Context;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Controller class for querying IMS status
+ */
+abstract class ImsQueryController {
+
+ @VisibleForTesting
+ ImsDirectQuery isSystemTtyEnabled(Context context) {
+ return new ImsQuerySystemTtyStat(context);
+ }
+
+ @VisibleForTesting
+ ImsDirectQuery isTtyOnVolteEnabled(int subId) {
+ return new ImsQueryTtyOnVolteStat(subId);
+ }
+}
diff --git a/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java b/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java
new file mode 100644
index 0000000..4239b16
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQuerySystemTtyStat.java
@@ -0,0 +1,47 @@
+/*
+ * 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.network.ims;
+
+import android.content.Context;
+import android.telecom.TelecomManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing system TTY stat
+ */
+public class ImsQuerySystemTtyStat extends ImsDirectQueryImpl {
+
+ /**
+ * Constructor
+ * @param context context of activity
+ */
+ public ImsQuerySystemTtyStat(Context context) {
+ mContext = context;
+ }
+
+ private volatile Context mContext;
+
+ /**
+ * Query running within a {@code Callable}
+ *
+ * @return result of query
+ */
+ public Boolean call() {
+ final TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+ return (telecomManager.getCurrentTtyMode() != TelecomManager.TTY_MODE_OFF);
+ }
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
new file mode 100644
index 0000000..a371ce7
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryTtyOnVolteStat.java
@@ -0,0 +1,46 @@
+/*
+ * 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.network.ims;
+
+import android.telephony.ims.ImsMmTelManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing IMS tty on VoLte stat
+ */
+public class ImsQueryTtyOnVolteStat extends ImsDirectQueryImpl {
+
+ /**
+ * Constructor
+ * @param subId subscription id
+ */
+ public ImsQueryTtyOnVolteStat(int subId) {
+ mSubId = subId;
+ }
+
+ private volatile int mSubId;
+
+ /**
+ * Query running within a {@code Callable}
+ *
+ * @return result of query
+ */
+ public Boolean call() {
+ final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+ return imsMmTelManager.isTtyOverVolteEnabled();
+ }
+}
diff --git a/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
new file mode 100644
index 0000000..86e25e6
--- /dev/null
+++ b/src/com/android/settings/network/ims/ImsQueryWfcUserSetting.java
@@ -0,0 +1,46 @@
+/*
+ * 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.network.ims;
+
+import android.telephony.ims.ImsMmTelManager;
+
+
+/**
+ * An {@code ImsQuery} for accessing IMS WFC enabled settings from user
+ */
+public class ImsQueryWfcUserSetting extends ImsDirectQueryImpl {
+
+ /**
+ * Constructor
+ * @param subId subscription id
+ */
+ public ImsQueryWfcUserSetting(int subId) {
+ mSubId = subId;
+ }
+
+ private volatile int mSubId;
+
+ /**
+ * Query running within a {@code Callable}
+ *
+ * @return result of query
+ */
+ public Boolean call() {
+ final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+ return imsMmTelManager.isVoWiFiSettingEnabled();
+ }
+}
diff --git a/src/com/android/settings/network/ims/VolteQueryImsState.java b/src/com/android/settings/network/ims/VolteQueryImsState.java
new file mode 100644
index 0000000..b999cda
--- /dev/null
+++ b/src/com/android/settings/network/ims/VolteQueryImsState.java
@@ -0,0 +1,72 @@
+/*
+ * 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.network.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.ims.ImsManager;
+import com.android.settings.network.SubscriptionUtil;
+
+
+/**
+ * Controller class for querying Volte status
+ */
+public class VolteQueryImsState extends ImsQueryController {
+
+ private Context mContext;
+ private int mSubId;
+
+ /**
+ * Constructor
+ *
+ * @param context {@code Context}
+ * @param subId subscription's id
+ */
+ public VolteQueryImsState(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Get allowance status for user to alter configuration
+ *
+ * @return true when changing configuration by user is allowed.
+ */
+ public boolean isAllowUserControl() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+
+ return ((!isSystemTtyEnabled(mContext).directQuery())
+ || (isTtyOnVolteEnabled(mSubId).directQuery()));
+ }
+
+ /**
+ * Get user's configuration
+ *
+ * @return true when user's configuration is ON otherwise false.
+ */
+ public boolean isEnabledByUser() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ ImsManager imsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
+ mContext, mSubId));
+ return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
+ }
+}
diff --git a/src/com/android/settings/network/ims/VtQueryImsState.java b/src/com/android/settings/network/ims/VtQueryImsState.java
new file mode 100644
index 0000000..38306cd
--- /dev/null
+++ b/src/com/android/settings/network/ims/VtQueryImsState.java
@@ -0,0 +1,57 @@
+/*
+ * 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.network.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import com.android.ims.ImsManager;
+import com.android.settings.network.SubscriptionUtil;
+
+/**
+ * Controller class for querying VT status
+ */
+public class VtQueryImsState {
+
+ private Context mContext;
+ private int mSubId;
+
+ /**
+ * Constructor
+ *
+ * @param context {@code Context}
+ * @param subId subscription's id
+ */
+ public VtQueryImsState(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Get user's configuration
+ *
+ * @return true when user's configuration is ON otherwise false.
+ */
+ public boolean isEnabledByUser() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ ImsManager imsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
+ mContext, mSubId));
+ return imsManager.isVtEnabledByUser();
+ }
+}
diff --git a/src/com/android/settings/network/ims/WifiCallingQueryImsState.java b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java
new file mode 100644
index 0000000..f8b0767
--- /dev/null
+++ b/src/com/android/settings/network/ims/WifiCallingQueryImsState.java
@@ -0,0 +1,76 @@
+/*
+ * 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.network.ims;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Controller class for querying Wifi calling status
+ */
+public class WifiCallingQueryImsState extends ImsQueryController {
+
+ private Context mContext;
+ private int mSubId;
+
+ /**
+ * Constructor
+ *
+ * @param context {@code Context}
+ * @param subId subscription's id
+ */
+ public WifiCallingQueryImsState(Context context, int subId) {
+ mContext = context;
+ mSubId = subId;
+ }
+
+ /**
+ * Implementation of ImsQueryController#isEnabledByUser(int subId)
+ */
+ @VisibleForTesting
+ ImsDirectQuery isEnabledByUser(int subId) {
+ return new ImsQueryWfcUserSetting(subId);
+ }
+
+ /**
+ * Get allowance status for user to alter configuration
+ *
+ * @return true when changing configuration by user is allowed.
+ */
+ public boolean isAllowUserControl() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+
+ return ((!isSystemTtyEnabled(mContext).directQuery())
+ || (isTtyOnVolteEnabled(mSubId).directQuery()));
+ }
+
+ /**
+ * Get user's configuration
+ *
+ * @return true when user's configuration is ON otherwise false.
+ */
+ public boolean isEnabledByUser() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ return isEnabledByUser(mSubId).directQuery();
+ }
+}
diff --git a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
index eabebd1..123e100 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java
@@ -22,9 +22,11 @@
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -33,6 +35,7 @@
import com.android.ims.ImsManager;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -46,7 +49,10 @@
public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
- private Preference mPreference;
+ private static final String TAG = "Enhanced4g";
+
+ @VisibleForTesting
+ Preference mPreference;
private CarrierConfigManager mCarrierConfigManager;
private PersistableBundle mCarrierConfig;
@VisibleForTesting
@@ -70,12 +76,12 @@
}
public Enhanced4gBasePreferenceController init(int subId) {
- if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && mSubId == subId) {
+ if (SubscriptionManager.isValidSubscriptionId(mSubId) && mSubId == subId) {
return this;
}
mSubId = subId;
mCarrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
- if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
mImsManager = ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(
mContext, mSubId));
}
@@ -102,14 +108,16 @@
return CONDITIONALLY_UNAVAILABLE;
}
final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
- final boolean isVisible = subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ final VolteQueryImsState queryState = queryImsState(subId);
+ final boolean isVisible = SubscriptionManager.isValidSubscriptionId(subId)
&& mImsManager != null && carrierConfig != null
&& mImsManager.isVolteEnabledByPlatform()
&& isVolteProvisionedOnDevice(mSubId)
&& MobileNetworkUtils.isImsServiceStateReady(mImsManager)
&& !carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL);
return isVisible
- ? (isPrefEnabled() ? AVAILABLE : AVAILABLE_UNSEARCHABLE)
+ ? (isUserControlAllowed() && queryState.isAllowUserControl()
+ ? AVAILABLE : AVAILABLE_UNSEARCHABLE)
: CONDITIONALLY_UNAVAILABLE;
}
@@ -134,14 +142,28 @@
super.updateState(preference);
final SwitchPreference switchPreference = (SwitchPreference) preference;
- switchPreference.setEnabled(isPrefEnabled());
- switchPreference.setChecked(mImsManager.isEnhanced4gLteModeSettingEnabledByUser()
- && mImsManager.isNonTtyOrTtyOnVolteEnabled());
+ final VolteQueryImsState queryState = queryImsState(mSubId);
+ switchPreference.setEnabled(isUserControlAllowed()
+ && queryState.isAllowUserControl());
+ switchPreference.setChecked(queryState.isEnabledByUser()
+ && queryState.isAllowUserControl());
}
@Override
public boolean setChecked(boolean isChecked) {
- mImsManager.setEnhanced4gLteModeSetting(isChecked);
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+ if (imsMmTelManager == null) {
+ return false;
+ }
+ try {
+ imsMmTelManager.setAdvancedCallingSettingEnabled(isChecked);
+ } catch (IllegalArgumentException exception) {
+ Log.w(TAG, "fail to set VoLTE=" + isChecked + ". subId=" + mSubId, exception);
+ return false;
+ }
for (final On4gLteUpdateListener lsn : m4gLteListeners) {
lsn.on4gLteUpdated();
}
@@ -150,7 +172,8 @@
@Override
public boolean isChecked() {
- return mImsManager.isEnhanced4gLteModeSettingEnabledByUser();
+ final VolteQueryImsState queryState = queryImsState(mSubId);
+ return queryState.isEnabledByUser();
}
public Enhanced4gBasePreferenceController addListener(On4gLteUpdateListener lsn) {
@@ -166,17 +189,19 @@
return m4gCurrentMode == getMode();
}
- private boolean isPrefEnabled() {
- return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- && (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE)
- && mImsManager != null
- && mImsManager.isNonTtyOrTtyOnVolteEnabled()
+ @VisibleForTesting
+ VolteQueryImsState queryImsState(int subId) {
+ return new VolteQueryImsState(mContext, subId);
+ }
+
+ private boolean isUserControlAllowed() {
+ return (mCallState != null) && (mCallState == TelephonyManager.CALL_STATE_IDLE)
&& mCarrierConfig.getBoolean(
CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL);
}
private boolean isVolteProvisionedOnDevice(int subId) {
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
return true;
}
final ProvisioningManager provisioningMgr = getProvisioningManager(subId);
@@ -204,7 +229,7 @@
public void register(Context context, int subId) {
mTelephonyManager = context.getSystemService(TelephonyManager.class);
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
}
mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
diff --git a/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java b/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
index 9c5069b..3cad316 100644
--- a/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
+++ b/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelper.java
@@ -27,6 +27,7 @@
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -43,17 +44,10 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SliceBroadcastReceiver;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
/**
* Helper class to control slices for enhanced 4g LTE settings.
*/
@@ -75,11 +69,6 @@
public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY =
"android.settings.NETWORK_OPERATOR_SETTINGS";
- /**
- * Timeout for querying enhanced 4g lte setting from ims manager.
- */
- private static final int TIMEOUT_MILLIS = 2000;
-
private final Context mContext;
/**
@@ -118,7 +107,7 @@
public Slice createEnhanced4gLteSlice(Uri sliceUri) {
final int subId = getDefaultVoiceSubId();
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.d(TAG, "Invalid subscription Id");
return null;
}
@@ -140,29 +129,16 @@
return null;
}
+ final VolteQueryImsState queryState = queryImsState(subId);
try {
return getEnhanced4gLteSlice(sliceUri,
- isEnhanced4gLteModeEnabled(imsManager), subId);
- } catch (InterruptedException | TimeoutException | ExecutionException e) {
+ queryState.isEnabledByUser(), subId);
+ } catch (IllegalArgumentException e) {
Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e);
return null;
}
}
- private boolean isEnhanced4gLteModeEnabled(ImsManager imsManager)
- throws InterruptedException, ExecutionException, TimeoutException {
- final FutureTask<Boolean> isEnhanced4gLteOnTask = new FutureTask<>(new Callable<Boolean>() {
- @Override
- public Boolean call() {
- return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
- }
- });
- final ExecutorService executor = Executors.newSingleThreadExecutor();
- executor.execute(isEnhanced4gLteOnTask);
-
- return isEnhanced4gLteOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- }
-
/**
* Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle
* enables/disables Enhanced 4G LTE mode setting.
@@ -206,23 +182,45 @@
public void handleEnhanced4gLteChanged(Intent intent) {
final int subId = getDefaultVoiceSubId();
- if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
final ImsManager imsManager = getImsManager(subId);
if (imsManager.isVolteEnabledByPlatform() && isVolteProvisionedOnDevice(subId)) {
- final boolean currentValue = imsManager.isEnhanced4gLteModeSettingEnabledByUser()
- && imsManager.isNonTtyOrTtyOnVolteEnabled();
+ final VolteQueryImsState queryState = queryImsState(subId);
+ final boolean currentValue = queryState.isEnabledByUser()
+ && queryState.isAllowUserControl();
final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
currentValue);
if (newValue != currentValue) {
- imsManager.setEnhanced4gLteModeSetting(newValue);
+ setEnhanced4gLteModeSetting(subId, newValue);
}
}
}
+ notifyEnhanced4gLteUpdate();
+ }
+
+ private void notifyEnhanced4gLteUpdate() {
// notify change in slice in any case to get re-queried. This would result in displaying
// appropriate message with the updated setting.
mContext.getContentResolver().notifyChange(CustomSliceRegistry.ENHANCED_4G_SLICE_URI, null);
}
+ @VisibleForTesting
+ void setEnhanced4gLteModeSetting(int subId, boolean isEnabled) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return;
+ }
+ final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(subId);
+ if (imsMmTelManager == null) {
+ return;
+ }
+ try {
+ imsMmTelManager.setAdvancedCallingSettingEnabled(isEnabled);
+ } catch (IllegalArgumentException exception) {
+ Log.w(TAG, "Unable to change the Enhanced 4g LTE to " + isEnabled + ". subId=" + subId,
+ exception);
+ }
+ }
+
private CharSequence getEnhanced4glteModeTitle(int subId) {
CharSequence ret = mContext.getText(R.string.enhanced_4g_lte_mode_title);
try {
@@ -296,5 +294,10 @@
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE);
}
+
+ @VisibleForTesting
+ VolteQueryImsState queryImsState(int subId) {
+ return new VolteQueryImsState(mContext, subId);
+ }
}
diff --git a/src/com/android/settings/network/telephony/MobileDataSlice.java b/src/com/android/settings/network/telephony/MobileDataSlice.java
index 40c747c..b76eb38 100644
--- a/src/com/android/settings/network/telephony/MobileDataSlice.java
+++ b/src/com/android/settings/network/telephony/MobileDataSlice.java
@@ -39,6 +39,7 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.network.MobileDataContentObserver;
+import com.android.settings.network.SubscriptionUtil;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
@@ -177,7 +178,7 @@
*/
private boolean isMobileDataAvailable() {
final List<SubscriptionInfo> subInfoList =
- mSubscriptionManager.getSelectableSubscriptionInfoList();
+ SubscriptionUtil.getSelectableSubscriptionInfoList(mContext);
return !(subInfoList == null || subInfoList.isEmpty());
}
diff --git a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
index 37cfc07..2973020 100644
--- a/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/VideoCallingPreferenceController.java
@@ -22,9 +22,11 @@
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -34,6 +36,8 @@
import com.android.ims.ImsManager;
import com.android.settings.network.MobileDataEnabledListener;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.VolteQueryImsState;
+import com.android.settings.network.ims.VtQueryImsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -46,6 +50,8 @@
MobileDataEnabledListener.Client,
Enhanced4gBasePreferenceController.On4gLteUpdateListener {
+ private static final String TAG = "VideoCallingPreference";
+
private Preference mPreference;
private CarrierConfigManager mCarrierConfigManager;
@VisibleForTesting
@@ -64,7 +70,7 @@
@Override
public int getAvailabilityStatus(int subId) {
- return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ return SubscriptionManager.isValidSubscriptionId(subId)
&& isVideoCallEnabled(subId)
? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
@@ -98,28 +104,41 @@
final boolean videoCallEnabled = isVideoCallEnabled(mSubId, mImsManager);
switchPreference.setVisible(videoCallEnabled);
if (videoCallEnabled) {
- final boolean is4gLteEnabled = mImsManager.isEnhanced4gLteModeSettingEnabledByUser()
+ final boolean videoCallEditable = queryVoLteState(mSubId).isEnabledByUser()
&& mImsManager.isNonTtyOrTtyOnVolteEnabled();
- preference.setEnabled(is4gLteEnabled &&
- mCallState == TelephonyManager.CALL_STATE_IDLE);
- switchPreference.setChecked(is4gLteEnabled && mImsManager.isVtEnabledByUser());
+ preference.setEnabled(videoCallEditable
+ && mCallState == TelephonyManager.CALL_STATE_IDLE);
+ switchPreference.setChecked(videoCallEditable && isChecked());
}
}
@Override
public boolean setChecked(boolean isChecked) {
- mImsManager.setVtSetting(isChecked);
- return true;
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return false;
+ }
+ final ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(mSubId);
+ if (imsMmTelManager == null) {
+ return false;
+ }
+ try {
+ imsMmTelManager.setVtSettingEnabled(isChecked);
+ return true;
+ } catch (IllegalArgumentException exception) {
+ Log.w(TAG, "Unable to set VT status " + isChecked + ". subId=" + mSubId,
+ exception);
+ }
+ return false;
}
@Override
public boolean isChecked() {
- return mImsManager.isVtEnabledByUser();
+ return queryImsState(mSubId).isEnabledByUser();
}
public VideoCallingPreferenceController init(int subId) {
mSubId = subId;
- if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
mImsManager = ImsManager.getInstance(mContext,
SubscriptionUtil.getPhoneId(mContext, mSubId));
}
@@ -128,7 +147,7 @@
}
private boolean isVideoCallEnabled(int subId) {
- final ImsManager imsManager = subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ final ImsManager imsManager = SubscriptionManager.isValidSubscriptionId(subId)
? ImsManager.getInstance(mContext, SubscriptionUtil.getPhoneId(mContext, subId))
: null;
return isVideoCallEnabled(subId, imsManager);
@@ -156,7 +175,7 @@
boolean isVideoCallEnabled(int subId, ImsManager imsManager) {
final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
telephonyManager = telephonyManager.createForSubscriptionId(subId);
}
return carrierConfig != null && imsManager != null
@@ -189,7 +208,7 @@
public void register(Context context, int subId) {
mTelephonyManager = context.getSystemService(TelephonyManager.class);
- if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
mTelephonyManager = mTelephonyManager.createForSubscriptionId(subId);
}
mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
@@ -207,4 +226,14 @@
public void onMobileDataEnabledChange() {
updateState(mPreference);
}
+
+ @VisibleForTesting
+ VtQueryImsState queryImsState(int subId) {
+ return new VtQueryImsState(mContext, subId);
+ }
+
+ @VisibleForTesting
+ VolteQueryImsState queryVoLteState(int subId) {
+ return new VolteQueryImsState(mContext, subId);
+ }
}
diff --git a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
index 9c9bf2f..ec23465 100644
--- a/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
+++ b/src/com/android/settings/network/telephony/WifiCallingPreferenceController.java
@@ -37,6 +37,7 @@
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -49,6 +50,8 @@
public class WifiCallingPreferenceController extends TelephonyBasePreferenceController implements
LifecycleObserver, OnStart, OnStop {
+ private static final String TAG = "WifiCallingPreference";
+
@VisibleForTesting
Integer mCallState;
@VisibleForTesting
@@ -69,7 +72,7 @@
@Override
public int getAvailabilityStatus(int subId) {
- return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ return SubscriptionManager.isValidSubscriptionId(subId)
&& MobileNetworkUtils.isWifiCallingEnabled(mContext, subId)
? AVAILABLE
: UNSUPPORTED_ON_DEVICE;
@@ -101,6 +104,7 @@
if (mCallState == null) {
return;
}
+ CharSequence summaryText = null;
if (mSimCallManager != null) {
final Intent intent = MobileNetworkUtils.buildPhoneAccountConfigureIntent(mContext,
mSimCallManager);
@@ -111,48 +115,51 @@
final PackageManager pm = mContext.getPackageManager();
final List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
preference.setTitle(resolutions.get(0).loadLabel(pm));
- preference.setSummary(null);
preference.setIntent(intent);
} else {
final String title = SubscriptionManager.getResourcesForSubId(mContext, mSubId)
.getString(R.string.wifi_calling_settings_title);
preference.setTitle(title);
- int resId = com.android.internal.R.string.wifi_calling_off_summary;
- if (mImsManager.isWfcEnabledByUser()) {
- boolean useWfcHomeModeForRoaming = false;
- if (mCarrierConfigManager != null) {
- final PersistableBundle carrierConfig =
- mCarrierConfigManager.getConfigForSubId(mSubId);
- if (carrierConfig != null) {
- useWfcHomeModeForRoaming = carrierConfig.getBoolean(
- CarrierConfigManager
- .KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL);
- }
- }
- final boolean isRoaming = getTelephonyManager(mContext, mSubId)
- .isNetworkRoaming();
- final int wfcMode = (isRoaming && !useWfcHomeModeForRoaming)
- ? mImsMmTelManager.getVoWiFiRoamingModeSetting() :
- mImsMmTelManager.getVoWiFiModeSetting();
- switch (wfcMode) {
- case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
- resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
- break;
- case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
- resId = com.android.internal.R.string
- .wfc_mode_cellular_preferred_summary;
- break;
- case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
- resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
- break;
- default:
- break;
+ summaryText = getResourceIdForWfcMode(mSubId);
+ }
+ preference.setSummary(summaryText);
+ preference.setEnabled(mCallState == TelephonyManager.CALL_STATE_IDLE);
+ }
+
+ private CharSequence getResourceIdForWfcMode(int subId) {
+ int resId = com.android.internal.R.string.wifi_calling_off_summary;
+ if (queryImsState(subId).isEnabledByUser()) {
+ boolean useWfcHomeModeForRoaming = false;
+ if (mCarrierConfigManager != null) {
+ final PersistableBundle carrierConfig =
+ mCarrierConfigManager.getConfigForSubId(subId);
+ if (carrierConfig != null) {
+ useWfcHomeModeForRoaming = carrierConfig.getBoolean(
+ CarrierConfigManager
+ .KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL);
}
}
- preference.setSummary(
- SubscriptionManager.getResourcesForSubId(mContext, mSubId).getText(resId));
+ final boolean isRoaming = getTelephonyManager(mContext, subId)
+ .isNetworkRoaming();
+ final int wfcMode = (isRoaming && !useWfcHomeModeForRoaming)
+ ? mImsMmTelManager.getVoWiFiRoamingModeSetting() :
+ mImsMmTelManager.getVoWiFiModeSetting();
+ switch (wfcMode) {
+ case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
+ resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
+ break;
+ case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED:
+ resId = com.android.internal.R.string
+ .wfc_mode_cellular_preferred_summary;
+ break;
+ case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED:
+ resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary;
+ break;
+ default:
+ break;
+ }
}
- preference.setEnabled(mCallState == TelephonyManager.CALL_STATE_IDLE);
+ return SubscriptionManager.getResourcesForSubId(mContext, subId).getText(resId);
}
public WifiCallingPreferenceController init(int subId) {
@@ -166,14 +173,22 @@
return this;
}
+ @VisibleForTesting
+ WifiCallingQueryImsState queryImsState(int subId) {
+ return new WifiCallingQueryImsState(mContext, subId);
+ }
+
protected ImsMmTelManager getImsMmTelManager(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return null;
+ }
return ImsMmTelManager.createForSubscriptionId(subId);
}
@VisibleForTesting
TelephonyManager getTelephonyManager(Context context, int subId) {
final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
- if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
return telephonyMgr;
}
final TelephonyManager subscriptionTelephonyMgr =
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index a8396a1..f749764 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -21,6 +21,7 @@
import android.app.INotificationManager;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.NotificationHistory;
import android.app.role.RoleManager;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
@@ -367,6 +368,15 @@
return false;
}
+ public NotificationHistory getNotificationHistory(String pkg) {
+ try {
+ return sINM.getNotificationHistory(pkg);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ }
+ return new NotificationHistory();
+ }
+
protected void recordAggregatedUsageEvents(Context context, AppRow appRow) {
long now = System.currentTimeMillis();
long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
diff --git a/src/com/android/settings/notification/history/HistoryLoader.java b/src/com/android/settings/notification/history/HistoryLoader.java
new file mode 100644
index 0000000..046b5ef
--- /dev/null
+++ b/src/com/android/settings/notification/history/HistoryLoader.java
@@ -0,0 +1,93 @@
+/*
+ * 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.notification.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class HistoryLoader {
+ private final Context mContext;
+ private final NotificationBackend mBackend;
+ private final PackageManager mPm;
+
+ public HistoryLoader(Context context, NotificationBackend backend, PackageManager pm) {
+ mContext = context;
+ mBackend = backend;
+ mPm = pm;
+ }
+
+ public void load(OnHistoryLoaderListener listener) {
+ ThreadUtils.postOnBackgroundThread(() -> {
+ Map<String, NotificationHistoryPackage> historicalNotifications = new HashMap<>();
+ NotificationHistory history =
+ mBackend.getNotificationHistory(mContext.getPackageName());
+
+ while (history.hasNextNotification()) {
+ HistoricalNotification hn = history.getNextNotification();
+
+ String key = hn.getPackage() + "|" + hn.getUid();
+ NotificationHistoryPackage hnsForPackage = historicalNotifications.getOrDefault(
+ key,
+ new NotificationHistoryPackage(hn.getPackage(), hn.getUid()));
+ hnsForPackage.notifications.add(hn);
+ historicalNotifications.put(key, hnsForPackage);
+ }
+ List<NotificationHistoryPackage> packages =
+ new ArrayList<>(historicalNotifications.values());
+ Collections.sort(packages,
+ (o1, o2) -> -1 * Long.compare(o1.getMostRecent(), o2.getMostRecent()));
+ for (NotificationHistoryPackage nhp : packages) {
+ ApplicationInfo info;
+ try {
+ info = mPm.getApplicationInfoAsUser(
+ nhp.pkgName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE,
+ UserHandle.getUserId(nhp.uid));
+ if (info != null) {
+ nhp.label = String.valueOf(mPm.getApplicationLabel(info));
+ nhp.icon = mPm.getUserBadgedIcon(mPm.getApplicationIcon(info),
+ UserHandle.of(UserHandle.getUserId(nhp.uid)));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // app is gone, just show package name and generic icon
+ nhp.icon = mPm.getDefaultActivityIcon();
+ }
+ }
+ ThreadUtils.postOnMainThread(() -> listener.onHistoryLoaded(packages));
+ });
+ }
+
+ interface OnHistoryLoaderListener {
+ void onHistoryLoaded(List<NotificationHistoryPackage> notificationsByPackage);
+ }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryActivity.java b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
new file mode 100644
index 0000000..173508b
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryActivity.java
@@ -0,0 +1,156 @@
+/*
+ * 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.notification.history;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.INotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+
+import java.util.Arrays;
+
+public class NotificationHistoryActivity extends Activity {
+
+ private static String TAG = "NotifHistory";
+
+ private ViewGroup mTodayView;
+ private ViewGroup mSnoozeView;
+ private ViewGroup mDismissView;
+ private HistoryLoader mHistoryLoader;
+ private INotificationManager mNm;
+ private PackageManager mPm;
+
+ private HistoryLoader.OnHistoryLoaderListener mOnHistoryLoaderListener = notifications -> {
+ // for each package, new header and recycler view
+ for (NotificationHistoryPackage nhp : notifications) {
+ View viewForPackage = LayoutInflater.from(this)
+ .inflate(R.layout.notification_history_app_layout, null);
+
+ final View container = viewForPackage.findViewById(R.id.list_container);
+ container.setVisibility(View.GONE);
+ ImageButton expand = viewForPackage.findViewById(R.id.expand);
+ expand.setOnClickListener(v -> {
+ container.setVisibility(container.getVisibility() == View.VISIBLE
+ ? View.GONE : View.VISIBLE);
+ expand.setImageResource(container.getVisibility() == View.VISIBLE
+ ? R.drawable.ic_expand_less
+ : com.android.internal.R.drawable.ic_expand_more);
+ });
+
+ TextView label = viewForPackage.findViewById(R.id.label);
+ label.setText(nhp.label != null ? nhp.label : nhp.pkgName);
+ ImageView icon = viewForPackage.findViewById(R.id.icon);
+ icon.setImageDrawable(nhp.icon);
+
+ RecyclerView rv = viewForPackage.findViewById(R.id.notification_list);
+ rv.setLayoutManager(new LinearLayoutManager(this));
+ rv.setAdapter(new NotificationHistoryAdapter());
+ ((NotificationHistoryAdapter) rv.getAdapter()).onRebuildComplete(nhp.notifications);
+ mTodayView.addView(viewForPackage);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.notification_history);
+ mTodayView = findViewById(R.id.apps);
+ mSnoozeView = findViewById(R.id.snoozed_list);
+ mDismissView = findViewById(R.id.recently_dismissed_list);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ mPm = getPackageManager();
+
+ mHistoryLoader = new HistoryLoader(this, new NotificationBackend(), mPm);
+ mHistoryLoader.load(mOnHistoryLoaderListener);
+
+ mNm = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ mListener.registerAsSystemService(this, new ComponentName(getPackageName(),
+ this.getClass().getCanonicalName()), ActivityManager.getCurrentUser());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot register listener", e);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ try {
+ mListener.unregisterAsSystemService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot unregister listener", e);
+ }
+ super.onPause();
+ }
+
+ private final NotificationListenerService mListener = new NotificationListenerService() {
+
+ @Override
+ public void onListenerConnected() {
+ StatusBarNotification[] snoozed = getSnoozedNotifications();
+ if (snoozed == null || snoozed.length == 0) {
+ mSnoozeView.setVisibility(View.GONE);
+ } else {
+ RecyclerView rv = mSnoozeView.findViewById(R.id.notification_list);
+ rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+ rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+ ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+ Arrays.asList(snoozed));
+ }
+
+ try {
+ StatusBarNotification[] dismissed = mNm.getHistoricalNotifications(
+ NotificationHistoryActivity.this.getPackageName(), 10);
+ RecyclerView rv = mDismissView.findViewById(R.id.notification_list);
+ rv.setLayoutManager(new LinearLayoutManager(NotificationHistoryActivity.this));
+ rv.setAdapter(new NotificationSbnAdapter(NotificationHistoryActivity.this, mPm));
+ ((NotificationSbnAdapter) rv.getAdapter()).onRebuildComplete(
+ Arrays.asList(dismissed));
+ mDismissView.setVisibility(View.VISIBLE);
+ } catch (Exception e) {
+ Slog.e(TAG, "Cannot load recently dismissed", e);
+ mDismissView.setVisibility(View.GONE);
+ }
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryAdapter.java b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
new file mode 100644
index 0000000..f87bb20
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryAdapter.java
@@ -0,0 +1,78 @@
+/*
+ * 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.notification.history;
+
+import android.app.NotificationHistory;
+import android.app.NotificationHistory.HistoricalNotification;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+import com.android.settings.notification.NotificationBackend;
+import com.android.settingslib.utils.ThreadUtils;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class NotificationHistoryAdapter extends
+ RecyclerView.Adapter<NotificationHistoryViewHolder> {
+
+ private List<HistoricalNotification> mValues;
+
+ public NotificationHistoryAdapter() {
+ mValues = new ArrayList<>();
+ setHasStableIds(true);
+ }
+
+ @Override
+ public NotificationHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.notification_history_log_row, parent, false);
+ return new NotificationHistoryViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final @NonNull NotificationHistoryViewHolder holder,
+ int position) {
+ final HistoricalNotification hn = mValues.get(position);
+ holder.setTitle(hn.getTitle());
+ holder.setSummary(hn.getText());
+ holder.setPostedTime(hn.getPostedTimeMs());
+ holder.addOnClick(hn.getPackage(), hn.getUserId(), hn.getChannelId());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mValues.size();
+ }
+
+ public void onRebuildComplete(List<HistoricalNotification> notifications) {
+ mValues = notifications;
+ notifyDataSetChanged();
+ }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryPackage.java b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
new file mode 100644
index 0000000..5170b75
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryPackage.java
@@ -0,0 +1,62 @@
+/*
+ * 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.notification.history;
+
+import android.app.NotificationHistory;
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+public class NotificationHistoryPackage {
+ String pkgName;
+ int uid;
+ List<NotificationHistory.HistoricalNotification> notifications;
+ CharSequence label;
+ Drawable icon;
+
+ public NotificationHistoryPackage(String pkgName, int uid) {
+ this.pkgName = pkgName;
+ this.uid = uid;
+ notifications = new ArrayList<>();
+ }
+
+ public long getMostRecent() {
+ if (notifications.isEmpty()) {
+ return 0;
+ }
+ return notifications.get(0).getPostedTimeMs();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NotificationHistoryPackage that = (NotificationHistoryPackage) o;
+ return uid == that.uid &&
+ Objects.equals(pkgName, that.pkgName) &&
+ Objects.equals(notifications, that.notifications) &&
+ Objects.equals(label, that.label) &&
+ Objects.equals(icon, that.icon);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(pkgName, uid, notifications, label, icon);
+ }
+}
diff --git a/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
new file mode 100644
index 0000000..35f5615
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationHistoryViewHolder.java
@@ -0,0 +1,70 @@
+/*
+ * 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.notification.history;
+
+import static android.provider.Settings.EXTRA_APP_PACKAGE;
+import static android.provider.Settings.EXTRA_CHANNEL_ID;
+
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationHistoryViewHolder extends RecyclerView.ViewHolder {
+
+ private final DateTimeView mTime;
+ private final TextView mTitle;
+ private final TextView mSummary;
+
+ NotificationHistoryViewHolder(View itemView) {
+ super(itemView);
+ mTime = itemView.findViewById(R.id.timestamp);
+ mTime.setShowRelativeTime(true);
+ mTitle = itemView.findViewById(R.id.title);
+ mSummary = itemView.findViewById(R.id.text);
+ }
+
+ void setSummary(CharSequence summary) {
+ mSummary.setText(summary);
+ mSummary.setVisibility(summary != null ? View.VISIBLE : View.GONE);
+ }
+
+ void setTitle(CharSequence title) {
+ mTitle.setText(title);
+ mTitle.setVisibility(title != null ? View.VISIBLE : View.GONE);
+ }
+
+ void setPostedTime(long postedTime) {
+ mTime.setTime(postedTime);
+ }
+
+ void addOnClick(String pkg, int userId, String channelId) {
+ itemView.setOnClickListener(v -> {
+ Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
+ .putExtra(EXTRA_APP_PACKAGE, pkg)
+ .putExtra(EXTRA_CHANNEL_ID, channelId);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ itemView.getContext().startActivityAsUser(intent, UserHandle.of(userId));
+ });
+ }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnAdapter.java b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
new file mode 100644
index 0000000..80ba278
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnAdapter.java
@@ -0,0 +1,152 @@
+/*
+ * 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.notification.history;
+
+import static android.content.pm.PackageManager.*;
+
+import android.app.Notification;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class NotificationSbnAdapter extends
+ RecyclerView.Adapter<NotificationSbnViewHolder> {
+
+ private static final String TAG = "SbnAdapter";
+ private List<StatusBarNotification> mValues;
+ private Map<Integer, Drawable> mUserBadgeCache;
+ private final Context mContext;
+ private PackageManager mPm;
+
+ public NotificationSbnAdapter(Context context, PackageManager pm) {
+ mContext = context;
+ mPm = pm;
+ mUserBadgeCache = new HashMap<>();
+ mValues = new ArrayList<>();
+ setHasStableIds(true);
+ }
+
+ @Override
+ public NotificationSbnViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.notification_sbn_log_row, parent, false);
+ return new NotificationSbnViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(final @NonNull NotificationSbnViewHolder holder,
+ int position) {
+ final StatusBarNotification sbn = mValues.get(position);
+ holder.setIcon(loadIcon(sbn));
+ holder.setPackageName(loadPackageName(sbn.getPackageName()).toString());
+ holder.setTitle(getTitleString(sbn.getNotification()));
+ holder.setSummary(getTextString(mContext, sbn.getNotification()));
+ holder.setPostedTime(sbn.getPostTime());
+ if (!mUserBadgeCache.containsKey(sbn.getUserId())) {
+ Drawable profile = mContext.getPackageManager().getUserBadgeForDensity(
+ UserHandle.of(sbn.getUserId()), -1);
+ mUserBadgeCache.put(sbn.getUserId(), profile);
+ }
+ holder.setProfileBadge(mUserBadgeCache.get(sbn.getUserId()));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mValues.size();
+ }
+
+ public void onRebuildComplete(List<StatusBarNotification> notifications) {
+ mValues = notifications;
+ notifyDataSetChanged();
+ }
+
+ private @NonNull CharSequence loadPackageName(String pkg) {
+ try {
+ ApplicationInfo info = mPm.getApplicationInfo(pkg,
+ MATCH_ANY_USER);
+ if (info != null) return mPm.getApplicationLabel(info);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Cannot load package name", e);
+ }
+ return pkg;
+ }
+
+ private static String getTitleString(Notification n) {
+ CharSequence title = null;
+ if (n.extras != null) {
+ title = n.extras.getCharSequence(Notification.EXTRA_TITLE);
+ }
+ return title == null? null : String.valueOf(title);
+ }
+
+ /**
+ * Returns the appropriate substring for this notification based on the style of notification.
+ */
+ private static String getTextString(Context appContext, Notification n) {
+ CharSequence text = null;
+ if (n.extras != null) {
+ text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+
+ Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+
+ if (nb.getStyle() instanceof Notification.BigTextStyle) {
+ text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
+ } else if (nb.getStyle() instanceof Notification.MessagingStyle) {
+ Notification.MessagingStyle ms = (Notification.MessagingStyle) nb.getStyle();
+ final List<Notification.MessagingStyle.Message> messages = ms.getMessages();
+ if (messages != null && messages.size() > 0) {
+ text = messages.get(messages.size() - 1).getText();
+ }
+ }
+
+ if (TextUtils.isEmpty(text)) {
+ text = n.extras.getCharSequence(Notification.EXTRA_TEXT);
+ }
+ }
+ return text == null ? null : String.valueOf(text);
+ }
+
+ private Drawable loadIcon(StatusBarNotification sbn) {
+ Drawable draw = sbn.getNotification().getSmallIcon().loadDrawableAsUser(
+ sbn.getPackageContext(mContext), sbn.getUserId());
+ if (draw == null) {
+ return null;
+ }
+ draw.mutate();
+ draw.setColorFilter(sbn.getNotification().color, PorterDuff.Mode.SRC_ATOP);
+ return draw;
+ }
+}
diff --git a/src/com/android/settings/notification/history/NotificationSbnViewHolder.java b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
new file mode 100644
index 0000000..79cda7f
--- /dev/null
+++ b/src/com/android/settings/notification/history/NotificationSbnViewHolder.java
@@ -0,0 +1,77 @@
+/*
+ * 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.notification.history;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.DateTimeView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.settings.R;
+
+public class NotificationSbnViewHolder extends RecyclerView.ViewHolder {
+
+ private final TextView mPkgName;
+ private final ImageView mIcon;
+ private final DateTimeView mTime;
+ private final TextView mTitle;
+ private final TextView mSummary;
+ private final ImageView mProfileBadge;
+
+ NotificationSbnViewHolder(View itemView) {
+ super(itemView);
+ mPkgName = itemView.findViewById(R.id.pkgname);
+ mIcon = itemView.findViewById(R.id.icon);
+ mTime = itemView.findViewById(R.id.timestamp);
+ mTitle = itemView.findViewById(R.id.title);
+ mSummary = itemView.findViewById(R.id.text);
+ mProfileBadge = itemView.findViewById(R.id.profile_badge);
+ }
+
+ void setSummary(CharSequence summary) {
+ mSummary.setVisibility(TextUtils.isEmpty(summary) ? View.GONE : View.VISIBLE);
+ mSummary.setText(summary);
+ }
+
+ void setTitle(CharSequence title) {
+ if (title == null) {
+ return;
+ }
+ mTitle.setText(title);
+ }
+
+ void setIcon(Drawable icon) {
+ mIcon.setImageDrawable(icon);
+ }
+
+ void setPackageName(String pkg) {
+ mPkgName.setText(pkg);
+ }
+
+ void setPostedTime(long postedTime) {
+ mTime.setTime(postedTime);
+ }
+
+ void setProfileBadge(Drawable badge) {
+ mProfileBadge.setImageDrawable(badge);
+ }
+}
diff --git a/src/com/android/settings/panel/PanelContent.java b/src/com/android/settings/panel/PanelContent.java
index de3dc89..badaeb1 100644
--- a/src/com/android/settings/panel/PanelContent.java
+++ b/src/com/android/settings/panel/PanelContent.java
@@ -28,11 +28,13 @@
*/
public interface PanelContent extends Instrumentable {
+ int ICON_UNAVAILABLE = -1;
+
/**
* @return a icon resource for the title of the Panel.
*/
default int getIcon() {
- return -1;
+ return ICON_UNAVAILABLE;
}
/**
diff --git a/src/com/android/settings/panel/PanelFragment.java b/src/com/android/settings/panel/PanelFragment.java
index db01a28..40706fe 100644
--- a/src/com/android/settings/panel/PanelFragment.java
+++ b/src/com/android/settings/panel/PanelFragment.java
@@ -193,7 +193,7 @@
loadAllSlices();
final int iconRes = mPanel.getIcon();
- if (iconRes == -1) {
+ if (iconRes == PanelContent.ICON_UNAVAILABLE) {
mTitleView.setText(mPanel.getTitle());
} else {
mTitleView.setVisibility(View.GONE);
diff --git a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
index fb8d85e..b584ea1 100644
--- a/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
+++ b/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragment.java
@@ -53,15 +53,20 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.InstrumentedFragment;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* The Fragment list those networks, which is proposed by other app, to user, and handle user's
* choose on either saving those networks or rejecting the request.
*/
-public class AddAppNetworksFragment extends InstrumentedFragment {
+public class AddAppNetworksFragment extends InstrumentedFragment implements
+ WifiTracker.WifiListener {
public static final String TAG = "AddAppNetworksFragment";
// Possible result values in each item of the returned result list, which is used
@@ -78,8 +83,9 @@
private static final int MESSAGE_SHOW_SAVE_FAILED = 3;
private static final int MESSAGE_FINISH = 4;
- // Signal level for the constant signal icon.
- private static final int MAX_RSSI_SIGNAL_LEVEL = 4;
+ // Signal level for the initial signal icon.
+ @VisibleForTesting
+ static final int INITIAL_RSSI_SIGNAL_LEVEL = 0;
// Max networks count within one request
private static final int MAX_SPECIFIC_NETWORKS_COUNT = 5;
@@ -103,6 +109,8 @@
List<UiConfigurationItem> mUiToRequestedList;
@VisibleForTesting
List<Integer> mResultCodeArrayList;
+ @VisibleForTesting
+ WifiTracker mWifiTracker;
private boolean mIsSingleNetwork;
private boolean mAnyNetworkSavedSuccess;
@@ -156,6 +164,8 @@
@Nullable Bundle savedInstanceState) {
mActivity = getActivity();
mWifiManager = mActivity.getSystemService(WifiManager.class);
+ mWifiTracker = WifiTrackerFactory.create(mActivity.getApplication(), this,
+ getSettingsLifecycle(), true /* includeSaved */, true /* includeScans */);
return inflater.inflate(R.layout.wifi_add_app_networks, container, false);
}
@@ -223,7 +233,7 @@
mLayoutView.findViewById(R.id.single_network).setVisibility(View.VISIBLE);
// Show signal icon for single network case.
- setSingleNetworkSignalIcon();
+ updateSingleNetworkSignalIcon(INITIAL_RSSI_SIGNAL_LEVEL);
// Show the SSID of the proposed network.
((TextView) mLayoutView.findViewById(R.id.single_ssid)).setText(
mUiToRequestedList.get(0).mDisplayedSsid);
@@ -323,61 +333,51 @@
* networks, for creating UI to user.
*/
@VisibleForTesting
- void filterSavedNetworks(
- List<WifiConfiguration> savedWifiConfigurations) {
+ void filterSavedNetworks(List<WifiConfiguration> savedWifiConfigurations) {
if (mUiToRequestedList == null) {
mUiToRequestedList = new ArrayList<>();
} else {
mUiToRequestedList.clear();
}
- boolean foundInSavedList;
- boolean foundError;
- String displayedName = null;
int networkPositionInBundle = 0;
for (WifiNetworkSuggestion suggestion : mAllSpecifiedNetworksList) {
- foundInSavedList = false;
- foundError = false;
+ String displayedName = null;
+ boolean foundInSavedList = false;
- /**
+ /*
* If specified is passpoint network, need to check with the existing passpoint
* networks.
*/
- if (suggestion.passpointConfiguration != null) {
- if (!suggestion.passpointConfiguration.validate()) {
- foundError = true;
- } else {
- foundInSavedList = isSavedPasspointConfiguration(
- suggestion.passpointConfiguration);
- displayedName = suggestion.passpointConfiguration.getHomeSp().getFriendlyName();
- }
+ final PasspointConfiguration passpointConfig = suggestion.getPasspointConfiguration();
+ if (passpointConfig != null) {
+ foundInSavedList = isSavedPasspointConfiguration(passpointConfig);
+ displayedName = passpointConfig.getHomeSp().getFriendlyName();
} else {
- final WifiConfiguration specifiedConfig = suggestion.wifiConfiguration;
+ final WifiConfiguration specifiedConfig = suggestion.getWifiConfiguration();
displayedName = removeDoubleQuotes(specifiedConfig.SSID);
foundInSavedList = isSavedWifiConfiguration(specifiedConfig,
savedWifiConfigurations);
}
- if (foundError) {
- mResultCodeArrayList.set(networkPositionInBundle, RESULT_NETWORK_ADD_ERROR);
- } else if (foundInSavedList) {
+ if (foundInSavedList) {
// If this requested network already in the saved networks, mark this item in the
// result code list as existed.
mResultCodeArrayList.set(networkPositionInBundle, RESULT_NETWORK_ALREADY_EXISTS);
} else {
// Prepare to add to UI list to show to user
- UiConfigurationItem uiConfigurationIcon = new UiConfigurationItem(displayedName,
- suggestion, networkPositionInBundle);
- mUiToRequestedList.add(uiConfigurationIcon);
+ UiConfigurationItem uiConfigurationItem = new UiConfigurationItem(displayedName,
+ suggestion, networkPositionInBundle, INITIAL_RSSI_SIGNAL_LEVEL);
+ mUiToRequestedList.add(uiConfigurationItem);
}
networkPositionInBundle++;
}
}
- private void setSingleNetworkSignalIcon() {
+ private void updateSingleNetworkSignalIcon(int level) {
// TODO: Check level of the network to show signal icon.
final Drawable wifiIcon = mActivity.getDrawable(
- Utils.getWifiIconResource(MAX_RSSI_SIGNAL_LEVEL)).mutate();
+ Utils.getWifiIconResource(level)).mutate();
final Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
wifiIconDark.setTintList(
Utils.getColorAttr(mActivity, android.R.attr.colorControlNormal));
@@ -472,12 +472,14 @@
public final String mDisplayedSsid;
public final WifiNetworkSuggestion mWifiNetworkSuggestion;
public final int mIndex;
+ public int mLevel;
UiConfigurationItem(String displayedSsid, WifiNetworkSuggestion wifiNetworkSuggestion,
- int index) {
+ int index, int level) {
mDisplayedSsid = displayedSsid;
mWifiNetworkSuggestion = wifiNetworkSuggestion;
mIndex = index;
+ mLevel = level;
}
}
@@ -515,7 +517,8 @@
final PreferenceImageView imageView = view.findViewById(android.R.id.icon);
if (imageView != null) {
final Drawable drawable = getContext().getDrawable(
- com.android.settingslib.Utils.getWifiIconResource(MAX_RSSI_SIGNAL_LEVEL));
+ com.android.settingslib.Utils.getWifiIconResource(
+ uiConfigurationItem.mLevel));
drawable.setTintList(
com.android.settingslib.Utils.getColorAttr(getContext(),
android.R.attr.colorControlNormal));
@@ -596,11 +599,12 @@
* Call framework API to save single network.
*/
private void saveNetwork(int index) {
- if (mUiToRequestedList.get(index).mWifiNetworkSuggestion.passpointConfiguration != null) {
+ final PasspointConfiguration passpointConfig =
+ mUiToRequestedList.get(index).mWifiNetworkSuggestion.getPasspointConfiguration();
+ if (passpointConfig != null) {
// Save passpoint, if no IllegalArgumentException, then treat it as success.
try {
- mWifiManager.addOrUpdatePasspointConfiguration(mUiToRequestedList.get(
- index).mWifiNetworkSuggestion.passpointConfiguration);
+ mWifiManager.addOrUpdatePasspointConfiguration(passpointConfig);
mAnyNetworkSavedSuccess = true;
} catch (IllegalArgumentException e) {
mResultCodeArrayList.set(mUiToRequestedList.get(index).mIndex,
@@ -612,18 +616,17 @@
}
// Show saved or failed according to all results.
showSavedOrFail();
- return;
} else {
- final WifiConfiguration wifiConfiguration = mUiToRequestedList.get(
- index).mWifiNetworkSuggestion.wifiConfiguration;
+ final WifiConfiguration wifiConfiguration =
+ mUiToRequestedList.get(index).mWifiNetworkSuggestion.getWifiConfiguration();
wifiConfiguration.SSID = addQuotationIfNeeded(wifiConfiguration.SSID);
mWifiManager.save(wifiConfiguration, mSaveListener);
}
}
private void connectNetwork(int index) {
- final WifiConfiguration wifiConfiguration = mUiToRequestedList.get(
- index).mWifiNetworkSuggestion.wifiConfiguration;
+ final WifiConfiguration wifiConfiguration =
+ mUiToRequestedList.get(index).mWifiNetworkSuggestion.getWifiConfiguration();
mWifiManager.connect(wifiConfiguration, null /* ActionListener */);
}
@@ -700,4 +703,46 @@
break;
}
}
+
+ @Override
+ public void onWifiStateChanged(int state) {
+ // Do nothing
+ }
+
+ @Override
+ public void onConnectedChanged() {
+ // Do nothing
+ }
+
+ @VisibleForTesting
+ void updateScanResults(List<AccessPoint> allAccessPoints) {
+ if (mUiToRequestedList == null) {
+ // Nothing need to be updated.
+ return;
+ }
+
+ // Update the signal level of the UI networks.
+ for (UiConfigurationItem uiConfigurationItem : mUiToRequestedList) {
+ final Optional<AccessPoint> matchedAccessPoint = allAccessPoints
+ .stream()
+ .filter(accesspoint -> accesspoint.matches(
+ uiConfigurationItem.mWifiNetworkSuggestion.getWifiConfiguration()))
+ .findFirst();
+ uiConfigurationItem.mLevel =
+ matchedAccessPoint.isPresent() ? matchedAccessPoint.get().getLevel() : 0;
+ }
+
+ if (mIsSingleNetwork) {
+ updateSingleNetworkSignalIcon(mUiToRequestedList.get(0).mLevel);
+ } else {
+ if (mUiConfigurationItemAdapter != null) {
+ mUiConfigurationItemAdapter.notifyDataSetChanged();
+ }
+ }
+ }
+
+ @Override
+ public void onAccessPointsChanged() {
+ updateScanResults(mWifiTracker.getAccessPoints());
+ }
}
diff --git a/src/com/android/settings/wifi/calling/OWNERS b/src/com/android/settings/wifi/calling/OWNERS
new file mode 100644
index 0000000..d23f816
--- /dev/null
+++ b/src/com/android/settings/wifi/calling/OWNERS
@@ -0,0 +1,8 @@
+# Default reviewers for this and subdirectories.
+allenwtsu@google.com
+andychou@google.com
+bonianchen@google.com
+songferngwang@google.com
+tomhsu@google.com
+
+# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
index ea34fce..cc9c22f 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSettingsForSub.java
@@ -46,7 +46,6 @@
import androidx.preference.PreferenceScreen;
import com.android.ims.ImsConfig;
-import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
@@ -56,6 +55,7 @@
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.widget.SwitchBar;
@@ -102,6 +102,7 @@
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ImsManager mImsManager;
private ImsMmTelManager mImsMmTelManager;
+ private ProvisioningManager mProvisioningManager;
private TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@@ -114,7 +115,8 @@
@Override
public void onCallStateChanged(int state, String incomingNumber) {
final SettingsActivity activity = (SettingsActivity) getActivity();
- final boolean isNonTtyOrTtyOnVolteEnabled = mImsManager.isNonTtyOrTtyOnVolteEnabled();
+ final boolean isNonTtyOrTtyOnVolteEnabled =
+ queryImsState(WifiCallingSettingsForSub.this.mSubId).isAllowUserControl();
final boolean isWfcEnabled = mSwitchBar.isChecked()
&& isNonTtyOrTtyOnVolteEnabled;
boolean isCallStateIdle =
@@ -246,6 +248,19 @@
}
@VisibleForTesting
+ WifiCallingQueryImsState queryImsState(int subId) {
+ return new WifiCallingQueryImsState(getContext(), subId);
+ }
+
+ @VisibleForTesting
+ ProvisioningManager getImsProvisioningManager() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return null;
+ }
+ return ProvisioningManager.createForSubscriptionId(mSubId);
+ }
+
+ @VisibleForTesting
ImsManager getImsManager() {
return ImsManager.getInstance(getActivity(),
SubscriptionUtil.getPhoneId(getActivity(), mSubId));
@@ -253,6 +268,9 @@
@VisibleForTesting
ImsMmTelManager getImsMmTelManager() {
+ if (!SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ return null;
+ }
return ImsMmTelManager.createForSubscriptionId(mSubId);
}
@@ -272,9 +290,10 @@
}
mImsManager = getImsManager();
+ mProvisioningManager = getImsProvisioningManager();
mImsMmTelManager = getImsMmTelManager();
- mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
+ mTelephonyManager = getActivity().getSystemService(TelephonyManager.class);
mButtonWfcMode = findPreference(BUTTON_WFC_MODE);
mButtonWfcMode.setOnPreferenceChangeListener(this);
@@ -342,7 +361,7 @@
}
}
- Resources res = getResourcesForSubId();
+ final Resources res = getResourcesForSubId();
mButtonWfcMode.setTitle(res.getString(R.string.wifi_calling_mode_title));
mButtonWfcMode.setDialogTitle(res.getString(R.string.wifi_calling_mode_dialog_title));
mButtonWfcRoamingMode.setTitle(res.getString(R.string.wifi_calling_roaming_mode_title));
@@ -383,8 +402,9 @@
}
// NOTE: Buttons will be enabled/disabled in mPhoneStateListener
- final boolean wfcEnabled = mImsManager.isWfcEnabledByUser()
- && mImsManager.isNonTtyOrTtyOnVolteEnabled();
+ final WifiCallingQueryImsState queryIms = queryImsState(mSubId);
+ final boolean wfcEnabled = queryIms.isEnabledByUser()
+ && queryIms.isAllowUserControl();
mSwitch.setChecked(wfcEnabled);
final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
@@ -397,8 +417,6 @@
public void onResume() {
super.onResume();
- final Context context = getActivity();
-
updateBody();
if (mImsManager.isWfcEnabledByPlatform()) {
@@ -409,6 +427,7 @@
mValidListener = true;
}
+ final Context context = getActivity();
context.registerReceiver(mIntentReceiver, mIntentFilter);
final Intent intent = getActivity().getIntent();
@@ -417,12 +436,7 @@
}
// Register callback for provisioning changes.
- try {
- mImsManager.getConfigInterface().addConfigCallback(mProvisioningCallback);
- } catch (ImsException e) {
- Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
- }
-
+ registerProvisioningChangedCallback();
}
@Override
@@ -442,13 +456,7 @@
context.unregisterReceiver(mIntentReceiver);
// Remove callback for provisioning changes.
- try {
- mImsManager.getConfigInterface().removeConfigCallback(
- mProvisioningCallback.getBinder());
- } catch (ImsException e) {
- Log.w(TAG, "onPause: Unable to remove callback for provisioning changes");
- }
-
+ unregisterProvisioningChangedCallback();
}
/**
@@ -508,7 +516,7 @@
*/
private void updateWfcMode(boolean wfcEnabled) {
Log.i(TAG, "updateWfcMode(" + wfcEnabled + ")");
- mImsManager.setWfcSetting(wfcEnabled);
+ mImsMmTelManager.setVoWiFiSettingEnabled(wfcEnabled);
final int wfcMode = mImsMmTelManager.getVoWiFiModeSetting();
final int wfcRoamingMode = mImsMmTelManager.getVoWiFiRoamingModeSetting();
@@ -524,8 +532,6 @@
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
- final Context context = getActivity();
-
Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
switch (requestCode) {
@@ -619,7 +625,7 @@
private CharSequence getWfcModeSummary(int wfcMode) {
int resId = com.android.internal.R.string.wifi_calling_off_summary;
- if (mImsManager.isWfcEnabledByUser()) {
+ if (queryImsState(mSubId).isEnabledByUser()) {
switch (wfcMode) {
case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
resId = com.android.internal.R.string.wfc_mode_wifi_only_summary;
@@ -641,4 +647,25 @@
Resources getResourcesForSubId() {
return SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
}
+
+ @VisibleForTesting
+ void registerProvisioningChangedCallback() {
+ if (mProvisioningManager == null) {
+ return;
+ }
+ try {
+ mProvisioningManager.registerProvisioningChangedCallback(getContext().getMainExecutor(),
+ mProvisioningCallback);
+ } catch (Exception ex) {
+ Log.w(TAG, "onResume: Unable to register callback for provisioning changes.");
+ }
+ }
+
+ @VisibleForTesting
+ void unregisterProvisioningChangedCallback() {
+ if (mProvisioningManager == null) {
+ return;
+ }
+ mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback);
+ }
}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
index 8ca9d5f..895bdba 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java
@@ -47,6 +47,7 @@
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.network.SubscriptionUtil;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.MobileNetworkUtils;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -133,9 +134,8 @@
*/
public Slice createWifiCallingSlice(Uri sliceUri) {
final int subId = getDefaultVoiceSubId();
- Resources res = getResourcesForSubId(subId);
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.d(TAG, "Invalid subscription Id");
return null;
}
@@ -148,42 +148,29 @@
return null;
}
- try {
- final boolean isWifiCallingEnabled = isWifiCallingEnabled(imsManager);
- final Intent activationAppIntent =
- getWifiCallingCarrierActivityIntent(subId);
+ final boolean isWifiCallingEnabled = isWifiCallingEnabled();
+ final Intent activationAppIntent =
+ getWifiCallingCarrierActivityIntent(subId);
- // Send this actionable wifi calling slice to toggle the setting
- // only when there is no need for wifi calling activation with the server
- if (activationAppIntent != null && !isWifiCallingEnabled) {
- Log.d(TAG, "Needs Activation");
- // Activation needed for the next action of the user
- // Give instructions to go to settings app
- return getNonActionableWifiCallingSlice(
- res.getText(R.string.wifi_calling_settings_title),
- res.getText(R.string.wifi_calling_settings_activation_instructions),
- sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
- }
- return getWifiCallingSlice(sliceUri, isWifiCallingEnabled, subId);
- } catch (InterruptedException | TimeoutException | ExecutionException e) {
- Log.e(TAG, "Unable to read the current WiFi calling status", e);
- return null;
+ // Send this actionable wifi calling slice to toggle the setting
+ // only when there is no need for wifi calling activation with the server
+ if (activationAppIntent != null && !isWifiCallingEnabled) {
+ Log.d(TAG, "Needs Activation");
+ // Activation needed for the next action of the user
+ // Give instructions to go to settings app
+ final Resources res = getResourcesForSubId(subId);
+ return getNonActionableWifiCallingSlice(
+ res.getText(R.string.wifi_calling_settings_title),
+ res.getText(R.string.wifi_calling_settings_activation_instructions),
+ sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY));
}
+ return getWifiCallingSlice(sliceUri, isWifiCallingEnabled, subId);
}
- private boolean isWifiCallingEnabled(ImsManager imsManager)
- throws InterruptedException, ExecutionException, TimeoutException {
- final FutureTask<Boolean> isWifiOnTask = new FutureTask<>(new Callable<Boolean>() {
- @Override
- public Boolean call() {
- return imsManager.isWfcEnabledByUser();
- }
- });
- final ExecutorService executor = Executors.newSingleThreadExecutor();
- executor.execute(isWifiOnTask);
-
- return isWifiOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
- && imsManager.isNonTtyOrTtyOnVolteEnabled();
+ private boolean isWifiCallingEnabled() {
+ final int subId = getDefaultVoiceSubId();
+ return queryImsState(subId).isEnabledByUser()
+ && queryImsState(subId).isAllowUserControl();
}
/**
@@ -192,7 +179,7 @@
*/
private Slice getWifiCallingSlice(Uri sliceUri, boolean isWifiCallingEnabled, int subId) {
final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
- Resources res = getResourcesForSubId(subId);
+ final Resources res = getResourcesForSubId(subId);
return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
@@ -228,7 +215,7 @@
public Slice createWifiCallingPreferenceSlice(Uri sliceUri) {
final int subId = getDefaultVoiceSubId();
- if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
Log.d(TAG, "Invalid Subscription Id");
return null;
}
@@ -254,7 +241,7 @@
boolean isWifiCallingEnabled = false;
int wfcMode = -1;
try {
- isWifiCallingEnabled = isWifiCallingEnabled(imsManager);
+ isWifiCallingEnabled = isWifiCallingEnabled();
wfcMode = getWfcMode(imsMmTelManager);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
Log.e(TAG, "Unable to get wifi calling preferred mode", e);
@@ -262,7 +249,7 @@
}
if (!isWifiCallingEnabled) {
// wifi calling is not enabled. Ask user to enable wifi calling
- Resources res = getResourcesForSubId(subId);
+ final Resources res = getResourcesForSubId(subId);
return getNonActionableWifiCallingSlice(
res.getText(R.string.wifi_calling_mode_title),
res.getText(R.string.wifi_calling_turn_on),
@@ -287,7 +274,7 @@
Uri sliceUri,
int subId) {
final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal);
- Resources res = getResourcesForSubId(subId);
+ final Resources res = getResourcesForSubId(subId);
// Top row shows information on current preference state
final ListBuilder listBuilder = new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext));
@@ -333,7 +320,7 @@
int preferenceTitleResId, String action, boolean checked, int subId) {
final IconCompat icon =
IconCompat.createWithResource(mContext, R.drawable.radio_button_check);
- Resources res = getResourcesForSubId(subId);
+ final Resources res = getResourcesForSubId(subId);
return new RowBuilder()
.setTitle(res.getText(preferenceTitleResId))
.setTitleItem(SliceAction.createToggle(getBroadcastIntent(action),
@@ -348,7 +335,7 @@
* @return summary/name of the wifi calling preference
*/
private CharSequence getWifiCallingPreferenceSummary(int wfcMode, int subId) {
- Resources res = getResourcesForSubId(subId);
+ final Resources res = getResourcesForSubId(subId);
switch (wfcMode) {
case ImsMmTelManager.WIFI_MODE_WIFI_ONLY:
return res.getText(
@@ -394,12 +381,12 @@
public void handleWifiCallingChanged(Intent intent) {
final int subId = getDefaultVoiceSubId();
- if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
final ImsManager imsManager = getImsManager(subId);
if (imsManager.isWfcEnabledByPlatform()
&& isWfcProvisionedOnDevice(subId)) {
- final boolean currentValue = imsManager.isWfcEnabledByUser()
- && imsManager.isNonTtyOrTtyOnVolteEnabled();
+ final boolean currentValue = queryImsState(subId).isEnabledByUser()
+ && queryImsState(subId).isAllowUserControl();
final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
currentValue);
final Intent activationAppIntent =
@@ -408,7 +395,8 @@
// If either the action is to turn off wifi calling setting
// or there is no activation involved - Update the setting
if (newValue != currentValue) {
- imsManager.setWfcSetting(newValue);
+ final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
+ imsMmTelManager.setVoWiFiSettingEnabled(newValue);
}
}
}
@@ -431,7 +419,7 @@
final int subId = getDefaultVoiceSubId();
final int errorValue = -1;
- if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ if (SubscriptionManager.isValidSubscriptionId(subId)) {
final boolean isWifiCallingPrefEditable = isCarrierConfigManagerKeyEnabled(
CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, subId, false);
final boolean isWifiOnlySupported = isCarrierConfigManagerKeyEnabled(
@@ -441,8 +429,8 @@
if (isWifiCallingPrefEditable
&& imsManager.isWfcEnabledByPlatform()
&& isWfcProvisionedOnDevice(subId)
- && imsManager.isWfcEnabledByUser()
- && imsManager.isNonTtyOrTtyOnVolteEnabled()) {
+ && queryImsState(subId).isEnabledByUser()
+ && queryImsState(subId).isAllowUserControl()) {
// Change the preference only when wifi calling is enabled
// And when wifi calling preference is editable for the current carrier
final ImsMmTelManager imsMmTelManager = getImsMmTelManager(subId);
@@ -587,4 +575,9 @@
private Resources getResourcesForSubId(int subId) {
return SubscriptionManager.getResourcesForSubId(mContext, subId);
}
+
+ @VisibleForTesting
+ WifiCallingQueryImsState queryImsState(int subId) {
+ return new WifiCallingQueryImsState(mContext, subId);
+ }
}
diff --git a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
index 526e9c3..0e3b887 100644
--- a/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
+++ b/src/com/android/settings/wifi/calling/WifiCallingSuggestionActivity.java
@@ -21,17 +21,20 @@
import com.android.ims.ImsManager;
import com.android.settings.SettingsActivity;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.network.telephony.MobileNetworkUtils;
public class WifiCallingSuggestionActivity extends SettingsActivity {
public static boolean isSuggestionComplete(Context context) {
+ final WifiCallingQueryImsState queryState =
+ new WifiCallingQueryImsState(context,
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
if (!ImsManager.isWfcEnabledByPlatform(context) ||
!MobileNetworkUtils.isWfcProvisionedOnDevice(
SubscriptionManager.getDefaultVoiceSubscriptionId())) {
return true;
}
- return ImsManager.isWfcEnabledByUser(context)
- && ImsManager.isNonTtyOrTtyOnVolteEnabled(context);
+ return queryState.isEnabledByUser() && queryState.isAllowUserControl();
}
}
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
index 5e69b8a..616c92f 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiScanWorker.java
@@ -62,4 +62,9 @@
}
return true;
}
+
+ @Override
+ protected int getApRowCount() {
+ return ContextualWifiSlice.getApRowCount();
+ }
}
\ No newline at end of file
diff --git a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
index 6b9dd3c..bb9be77 100644
--- a/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
+++ b/src/com/android/settings/wifi/slice/ContextualWifiSlice.java
@@ -17,30 +17,42 @@
package com.android.settings.wifi.slice;
import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.text.TextUtils;
-import android.util.Log;
import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
+import androidx.slice.builders.ListBuilder;
+import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
+import com.android.settingslib.wifi.AccessPoint;
/**
* {@link CustomSliceable} for Wi-Fi, used by contextual homepage.
*/
public class ContextualWifiSlice extends WifiSlice {
- private static final String TAG = "ContextualWifiSlice";
+ @VisibleForTesting
+ static final int COLLAPSED_ROW_COUNT = 0;
+
@VisibleForTesting
static long sActiveUiSession = -1000;
@VisibleForTesting
- static boolean sPreviouslyDisplayed;
+ static boolean sToggleNeeded = true;
public ContextualWifiSlice(Context context) {
super(context);
@@ -57,19 +69,77 @@
.getSlicesFeatureProvider().getUiSessionToken();
if (currentUiSession != sActiveUiSession) {
sActiveUiSession = currentUiSession;
- sPreviouslyDisplayed = false;
+ sToggleNeeded = !hasWorkingNetwork();
+ } else if (!mWifiManager.isWifiEnabled()) {
+ sToggleNeeded = true;
}
- if (!sPreviouslyDisplayed && hasWorkingNetwork()) {
- Log.d(TAG, "Wifi is connected, no point showing any suggestion.");
- return null;
- }
- // Set sPreviouslyDisplayed to true - we will show *something* on the screen. So we should
- // keep showing this card to keep UI stable, even if wifi connects to a network later.
- sPreviouslyDisplayed = true;
-
return super.getSlice();
}
+ static int getApRowCount() {
+ return sToggleNeeded ? DEFAULT_EXPANDED_ROW_COUNT : COLLAPSED_ROW_COUNT;
+ }
+
+ @Override
+ protected boolean isToggleNeeded() {
+ return sToggleNeeded;
+ }
+
+ @Override
+ protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
+ final ListBuilder.RowBuilder builder = super.getHeaderRow(accessPoint);
+ if (!sToggleNeeded) {
+ builder.setTitleItem(getLevelIcon(accessPoint), ListBuilder.ICON_IMAGE)
+ .setSubtitle(getSubtitle(accessPoint));
+ }
+ return builder;
+ }
+
+ private IconCompat getLevelIcon(AccessPoint accessPoint) {
+ if (accessPoint != null) {
+ return getAccessPointLevelIcon(accessPoint);
+ }
+
+ final Drawable drawable = mContext.getDrawable(
+ com.android.settingslib.Utils.getWifiIconResource(0));
+ final int color = Utils.getColorAttrDefaultColor(mContext,
+ android.R.attr.colorControlNormal);
+ drawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
+ return Utils.createIconWithDrawable(drawable);
+ }
+
+ private CharSequence getSubtitle(AccessPoint accessPoint) {
+ if (isCaptivePortal()) {
+ final int id = mContext.getResources()
+ .getIdentifier("network_available_sign_in", "string", "android");
+ return mContext.getText(id);
+ }
+
+ if (accessPoint == null) {
+ return mContext.getText(R.string.disconnected);
+ }
+
+ final NetworkInfo networkInfo = accessPoint.getNetworkInfo();
+ if (networkInfo == null) {
+ return mContext.getText(R.string.disconnected);
+ }
+
+ final State state = networkInfo.getState();
+ DetailedState detailedState;
+ if (state == State.CONNECTING) {
+ detailedState = DetailedState.CONNECTING;
+ } else if (state == State.CONNECTED) {
+ detailedState = DetailedState.CONNECTED;
+ } else {
+ detailedState = networkInfo.getDetailedState();
+ }
+
+ final String[] formats = mContext.getResources().getStringArray(
+ R.array.wifi_status_with_ssid);
+ final int index = detailedState.ordinal();
+ return String.format(formats[index], accessPoint.getTitle());
+ }
+
private boolean hasWorkingNetwork() {
return !TextUtils.equals(getActiveSSID(), WifiManager.UNKNOWN_SSID) && hasInternetAccess();
}
diff --git a/src/com/android/settings/wifi/slice/WifiScanWorker.java b/src/com/android/settings/wifi/slice/WifiScanWorker.java
index a5bd74d..be1e005 100644
--- a/src/com/android/settings/wifi/slice/WifiScanWorker.java
+++ b/src/com/android/settings/wifi/slice/WifiScanWorker.java
@@ -111,10 +111,11 @@
// AccessPoints are sorted by the WifiTracker
final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
final List<AccessPoint> resultList = new ArrayList<>();
+ final int apRowCount = getApRowCount();
for (AccessPoint ap : accessPoints) {
if (ap.isReachable()) {
resultList.add(clone(ap));
- if (resultList.size() >= DEFAULT_EXPANDED_ROW_COUNT) {
+ if (resultList.size() >= apRowCount) {
break;
}
}
@@ -122,6 +123,10 @@
updateResults(resultList);
}
+ protected int getApRowCount() {
+ return DEFAULT_EXPANDED_ROW_COUNT;
+ }
+
private AccessPoint clone(AccessPoint accessPoint) {
final Bundle savedState = new Bundle();
accessPoint.saveWifiState(savedState);
diff --git a/src/com/android/settings/wifi/slice/WifiSlice.java b/src/com/android/settings/wifi/slice/WifiSlice.java
index 97ac9c5..8e2d7a6 100644
--- a/src/com/android/settings/wifi/slice/WifiSlice.java
+++ b/src/com/android/settings/wifi/slice/WifiSlice.java
@@ -95,7 +95,7 @@
mContext.getTheme().applyStyle(R.style.Theme_Settings_Home, true /* force */);
final boolean isWifiEnabled = isWifiEnabled();
- ListBuilder listBuilder = getHeaderRow(isWifiEnabled);
+ ListBuilder listBuilder = getListBuilder(isWifiEnabled, null /* accessPoint */);
if (!isWifiEnabled) {
WifiScanWorker.clearClickedWifi();
return listBuilder.build();
@@ -107,17 +107,12 @@
final boolean isFirstApActive = apCount > 0 && apList.get(0).isActive();
handleNetworkCallback(worker, isFirstApActive);
- // Need a loading text when results are not ready or out of date.
- boolean needLoadingRow = true;
- // Skip checking the existence of the first access point if it's active
- int index = isFirstApActive ? 1 : 0;
- // This loop checks the existence of reachable APs to determine the validity of the current
- // AP list.
- for (; index < apCount; index++) {
- if (apList.get(index).isReachable()) {
- needLoadingRow = false;
- break;
+ if (!isToggleNeeded()) {
+ if (isFirstApActive) {
+ // refresh header subtext
+ listBuilder = getListBuilder(true /* isWifiEnabled */, apList.get(0));
}
+ return listBuilder.build();
}
// Add AP rows
@@ -125,9 +120,8 @@
for (int i = 0; i < DEFAULT_EXPANDED_ROW_COUNT; i++) {
if (i < apCount) {
listBuilder.addRow(getAccessPointRow(apList.get(i)));
- } else if (needLoadingRow) {
+ } else if (i == apCount) {
listBuilder.addRow(getLoadingRow(placeholder));
- needLoadingRow = false;
} else {
listBuilder.addRow(new ListBuilder.RowBuilder()
.setTitle(placeholder)
@@ -148,24 +142,34 @@
}
}
- private ListBuilder getHeaderRow(boolean isWifiEnabled) {
+ protected boolean isToggleNeeded() {
+ return true;
+ }
+
+ protected ListBuilder.RowBuilder getHeaderRow(AccessPoint accessPoint) {
final IconCompat icon = IconCompat.createWithResource(mContext,
R.drawable.ic_settings_wireless);
final String title = mContext.getString(R.string.wifi_settings);
- final PendingIntent toggleAction = getBroadcastIntent(mContext);
final PendingIntent primaryAction = getPrimaryAction();
final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon,
ListBuilder.ICON_IMAGE, title);
- final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction,
- null /* actionTitle */, isWifiEnabled);
- return new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
+ return new ListBuilder.RowBuilder()
+ .setTitle(title)
+ .setPrimaryAction(primarySliceAction);
+ }
+
+ private ListBuilder getListBuilder(boolean isWifiEnabled, AccessPoint accessPoint) {
+ final ListBuilder builder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY)
.setAccentColor(COLOR_NOT_TINTED)
.setKeywords(getKeywords())
- .addRow(new ListBuilder.RowBuilder()
- .setTitle(title)
- .addEndItem(toggleSliceAction)
- .setPrimaryAction(primarySliceAction));
+ .addRow(getHeaderRow(accessPoint));
+ if (isToggleNeeded()) {
+ final PendingIntent toggleAction = getBroadcastIntent(mContext);
+ builder.addAction(SliceAction.createToggle(toggleAction, null /* actionTitle */,
+ isWifiEnabled));
+ }
+ return builder;
}
private ListBuilder.RowBuilder getAccessPointRow(AccessPoint accessPoint) {
@@ -200,7 +204,7 @@
return TextUtils.isEmpty(summary) ? mContext.getText(R.string.disconnected) : summary;
}
- private IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) {
+ protected IconCompat getAccessPointLevelIcon(AccessPoint accessPoint) {
final Drawable d = mContext.getDrawable(
com.android.settingslib.Utils.getWifiIconResource(accessPoint.getLevel()));
@@ -250,24 +254,16 @@
accessPoint.saveWifiState(extras);
if (accessPoint.isActive()) {
- Intent intent;
+ final SubSettingLauncher launcher = new SubSettingLauncher(mContext)
+ .setTitleRes(R.string.pref_title_network_details)
+ .setArguments(extras)
+ .setSourceMetricsCategory(SettingsEnums.WIFI);
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_WIFITRACKER2)) {
- intent = new SubSettingLauncher(mContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment2.class.getName())
- .setArguments(extras)
- .setSourceMetricsCategory(SettingsEnums.WIFI)
- .toIntent();
- return getActivityAction(requestCode, intent, icon, title);
+ launcher.setDestination(WifiNetworkDetailsFragment2.class.getName());
} else {
- intent = new SubSettingLauncher(mContext)
- .setTitleRes(R.string.pref_title_network_details)
- .setDestination(WifiNetworkDetailsFragment.class.getName())
- .setArguments(extras)
- .setSourceMetricsCategory(SettingsEnums.WIFI)
- .toIntent();
- return getActivityAction(requestCode, intent, icon, title);
+ launcher.setDestination(WifiNetworkDetailsFragment.class.getName());
}
+ return getActivityAction(requestCode, launcher.toIntent(), icon, title);
} else if (WifiUtils.getConnectingType(accessPoint) != WifiUtils.CONNECT_TYPE_OTHERS) {
final Intent intent = new Intent(mContext, ConnectToWifiHandler.class)
.putExtra(WifiDialogActivity.KEY_ACCESS_POINT_STATE, extras);
@@ -307,7 +303,7 @@
.setSubtitle(title);
}
- private boolean isCaptivePortal() {
+ protected boolean isCaptivePortal() {
final NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
mWifiManager.getCurrentNetwork());
return WifiUtils.canSignIntoNetwork(nc);
diff --git a/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java
new file mode 100644
index 0000000..9316735
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/AppOpenSupportedLinksPreferenceControllerTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.applications;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+public class AppOpenSupportedLinksPreferenceControllerTest {
+ private static final String TEST_KEY = "test_key";
+ private static final String TEST_DOMAIN_LINK = "aaa.bbb.ccc";
+ private static final String TEST_PACKAGE = "ssl.test.package.com";
+
+ @Mock
+ private PackageManager mPackageManager;
+
+ private Context mContext;
+ private PreferenceManager mPreferenceManager;
+ private PreferenceScreen mScreen;
+ private PreferenceCategory mCategory;
+ private AppOpenSupportedLinksPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ mPreferenceManager = new PreferenceManager(mContext);
+ mScreen = spy(mPreferenceManager.createPreferenceScreen(mContext));
+ mCategory = spy(new PreferenceCategory(mContext));
+ mController = spy(new AppOpenSupportedLinksPreferenceController(mContext, TEST_KEY));
+ mController.setInit(TEST_PACKAGE);
+ }
+
+ @Test
+ public void displayPreference_statusAlways_allowOpenChecked() {
+ init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mController.mAllowOpening.isChecked()).isTrue();
+ assertThat(mController.mAskEveryTime.isChecked()).isFalse();
+ assertThat(mController.mNotAllowed.isChecked()).isFalse();
+ }
+
+ @Test
+ public void displayPreference_statusAsk_askEveryTimeChecked() {
+ init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mController.mAllowOpening.isChecked()).isFalse();
+ assertThat(mController.mAskEveryTime.isChecked()).isTrue();
+ assertThat(mController.mNotAllowed.isChecked()).isFalse();
+ }
+
+ @Test
+ public void displayPreference_statusNever_notAllowedChecked() {
+ init(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER);
+
+ mController.displayPreference(mScreen);
+
+ assertThat(mController.mAllowOpening.isChecked()).isFalse();
+ assertThat(mController.mAskEveryTime.isChecked()).isFalse();
+ assertThat(mController.mNotAllowed.isChecked()).isTrue();
+ }
+
+ @Test
+ @Config(shadows = ShadowUtils.class)
+ public void getEntriesNo_oneHandledDomains_returnOne() {
+ initHandledDomains();
+
+ assertThat(mController.getEntriesNo()).isEqualTo(1);
+ }
+
+ private void init(int status) {
+ doReturn(mCategory).when(mScreen).findPreference(any(CharSequence.class));
+ doReturn(true).when(mCategory).addPreference(any(Preference.class));
+ when(mPackageManager.getIntentVerificationStatusAsUser(anyString(), anyInt())).thenReturn(
+ status);
+ }
+
+ private void initHandledDomains() {
+ final ArraySet<String> domainLinks = new ArraySet<>();
+ domainLinks.add(TEST_DOMAIN_LINK);
+ ShadowUtils.setHandledDomains(domainLinks);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java b/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java
new file mode 100644
index 0000000..f9d4ca8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/applications/OpenSupportedLinksTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.applications;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.ArraySet;
+
+import com.android.settings.testutils.shadow.ShadowUtils;
+import com.android.settingslib.widget.FooterPreference;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
+public class OpenSupportedLinksTest {
+ private static final String TEST_FOOTER_TITLE = "FooterTitle";
+ private static final String TEST_DOMAIN_LINK = "aaa.bbb.ccc";
+
+ private Context mContext;
+ private TestFragment mSettings;
+ private FooterPreference mFooter;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ mSettings = spy(new TestFragment(mContext));
+ mFooter = new FooterPreference.Builder(mContext).setTitle(TEST_FOOTER_TITLE).build();
+ }
+
+ @After
+ public void tearDown() {
+ ShadowUtils.reset();
+ }
+
+ @Test
+ public void addLinksToFooter_noHandledDomains_returnDefaultFooterTitle() {
+ mSettings.addLinksToFooter(mFooter);
+
+ assertThat(mFooter.getTitle()).isEqualTo(TEST_FOOTER_TITLE);
+ }
+
+ @Test
+ public void addLinksToFooter_oneHandledDomains_returnDomainsFooterTitle() {
+ final ArraySet<String> domainLinks = new ArraySet<>();
+ domainLinks.add(TEST_DOMAIN_LINK);
+ ShadowUtils.setHandledDomains(domainLinks);
+
+ mSettings.addLinksToFooter(mFooter);
+
+ assertThat(mFooter.getTitle().toString()).contains(TEST_DOMAIN_LINK);
+ }
+
+ public static class TestFragment extends OpenSupportedLinks {
+ private final Context mContext;
+
+ public TestFragment(Context context) {
+ mContext = context;
+ mPackageInfo = new PackageInfo();
+ mPackageInfo.packageName = "ssl.test.package.com";
+ }
+
+ @Override
+ protected PackageManager getPackageManager() {
+ return mContext.getPackageManager();
+ }
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
index d0cb97a..e558d62 100644
--- a/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
+++ b/tests/robotests/src/com/android/settings/development/compat/PlatformCompatDashboardTest.java
@@ -105,6 +105,7 @@
mDashboard = spy(new PlatformCompatDashboard());
mDashboard.mSelectedApp = APP_NAME;
doReturn(mApplicationInfo).when(mDashboard).getApplicationInfo();
+ doReturn(mContext).when(mDashboard).getContext();
doReturn(mPlatformCompat).when(mDashboard).getPlatformCompat();
doReturn(mPreferenceScreen).when(mDashboard).getPreferenceScreen();
doReturn(mPreferenceManager).when(mDashboard).getPreferenceManager();
@@ -130,7 +131,8 @@
Preference appPreference = mDashboard.createAppPreference(mApplicationInfo);
- assertThat(appPreference.getSummary()).isEqualTo(APP_NAME + " SDK 1");
+ assertThat(appPreference.getSummary()).isEqualTo(mContext.getResources().getString(
+ R.string.platform_compat_selected_app_summary, APP_NAME, 1));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
index cd20ea2..25117be 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeActivationPreferenceControllerTest.java
@@ -32,7 +32,10 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
+import java.util.Locale;
+
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
@@ -60,9 +63,12 @@
private Button mTurnOnButton;
@Mock
private PowerManager mPM;
+ @Mock
+ private TimeFormatter mFormat;
- private Configuration configNightYes = new Configuration();
- private Configuration configNightNo = new Configuration();;
+ private Configuration mConfigNightYes = new Configuration();
+ private Configuration mConfigNightNo = new Configuration();
+ private Locale mLocal = new Locale("ENG");
@Before
public void setUp() {
@@ -77,6 +83,7 @@
when(mPreference.findViewById(
eq(R.id.dark_ui_turn_off_button))).thenReturn(mTurnOffButton);
when(mService.setNightModeActivated(anyBoolean())).thenReturn(true);
+ when(mFormat.of(any())).thenReturn("10:00 AM");
when(mContext.getString(
R.string.dark_ui_activation_off_auto)).thenReturn("off_auto");
when(mContext.getString(
@@ -93,16 +100,22 @@
R.string.dark_ui_summary_off_auto_mode_never)).thenReturn("summary_off_manual");
when(mContext.getString(
R.string.dark_ui_summary_on_auto_mode_never)).thenReturn("summary_on_manual");
- mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey);
+ when(mContext.getString(
+ R.string.dark_ui_summary_on_auto_mode_custom)).thenReturn("summary_on_custom");
+ when(mContext.getString(
+ R.string.dark_ui_summary_off_auto_mode_custom)).thenReturn("summary_off_custom");
+ mController = new DarkModeActivationPreferenceController(mContext, mPreferenceKey, mFormat);
mController.displayPreference(mScreen);
- configNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
- configNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
+ mConfigNightNo.uiMode = Configuration.UI_MODE_NIGHT_NO;
+ mConfigNightYes.uiMode = Configuration.UI_MODE_NIGHT_YES;
+ mConfigNightNo.locale = mLocal;
+ mConfigNightYes.locale = mLocal;
}
@Test
public void nightMode_toggleButton_offManual() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
- when(res.getConfiguration()).thenReturn(configNightYes);
+ when(res.getConfiguration()).thenReturn(mConfigNightYes);
mController.updateState(mPreference);
@@ -112,11 +125,37 @@
R.string.dark_ui_activation_off_manual)));
}
+ @Test
+ public void nightMode_toggleButton_offCustom() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+ when(res.getConfiguration()).thenReturn(mConfigNightYes);
+
+ mController.updateState(mPreference);
+
+ verify(mTurnOnButton).setVisibility(eq(View.GONE));
+ verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
+ verify(mTurnOffButton).setText(eq(mContext.getString(
+ R.string.dark_ui_activation_off_custom)));
+ }
+
+ @Test
+ public void nightMode_toggleButton_onCustom() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+ when(res.getConfiguration()).thenReturn(mConfigNightYes);
+
+ mController.updateState(mPreference);
+
+ verify(mTurnOnButton).setVisibility(eq(View.GONE));
+ verify(mTurnOffButton).setVisibility(eq(View.VISIBLE));
+ verify(mTurnOffButton).setText(eq(mContext.getString(
+ R.string.dark_ui_activation_on_custom)));
+ }
+
@Test
public void nightMode_toggleButton_onAutoWhenModeIsYes() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
- when(res.getConfiguration()).thenReturn(configNightNo);
+ when(res.getConfiguration()).thenReturn(mConfigNightNo);
mController.updateState(mPreference);
@@ -129,7 +168,7 @@
@Test
public void nightMode_toggleButton_onAutoWhenModeIsAuto() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
- when(res.getConfiguration()).thenReturn(configNightNo);
+ when(res.getConfiguration()).thenReturn(mConfigNightNo);
mController.updateState(mPreference);
@@ -142,7 +181,7 @@
@Test
public void nightModeSummary_buttonText_onManual() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_NO);
- when(res.getConfiguration()).thenReturn(configNightYes);
+ when(res.getConfiguration()).thenReturn(mConfigNightYes);
assertEquals(mController.getSummary(), mContext.getString(
R.string.dark_ui_summary_on_auto_mode_never));
@@ -151,7 +190,7 @@
@Test
public void nightModeSummary_buttonText_offAuto() {
when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_AUTO);
- when(res.getConfiguration()).thenReturn(configNightNo);
+ when(res.getConfiguration()).thenReturn(mConfigNightNo);
assertEquals(mController.getSummary(), mContext.getString(
R.string.dark_ui_summary_off_auto_mode_auto));
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java
new file mode 100644
index 0000000..107b79f
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeCustomPreferenceControllerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.display.darkmode;
+
+import android.app.UiModeManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import androidx.preference.Preference;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+public class DarkModeCustomPreferenceControllerTest {
+ private DarkModeCustomPreferenceController mController;
+ @Mock
+ private UiModeManager mService;
+ @Mock
+ private Context mContext;
+ @Mock
+ private Preference mPreference;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private ContentResolver mCR;
+ @Mock
+ private TimeFormatter mFormat;
+ @Mock
+ private DarkModeSettingsFragment mFragment;
+ private Configuration mConfig = new Configuration();
+
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mResources.getConfiguration()).thenReturn(mConfig);
+ when(mContext.getContentResolver()).thenReturn(mCR);
+ mService = mock(UiModeManager.class);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getSystemService(UiModeManager.class)).thenReturn(mService);
+ mController = new DarkModeCustomPreferenceController(mContext, "key", mFragment, mFormat);
+ when(mFormat.is24HourFormat()).thenReturn(false);
+ when(mFormat.of(any())).thenReturn("10:00 AM");
+ }
+
+ @Test
+ public void nightMode_customOff_hidePreference() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_YES);
+ mController.refreshSummary(mPreference);
+ verify(mPreference).setVisible(eq(false));
+ }
+
+ @Test
+ public void nightMode_customOff_showPreference() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+ mController.refreshSummary(mPreference);
+ verify(mPreference).setVisible(eq(true));
+ }
+
+ @Test
+ public void nightMode_customOff_setSummaryNotNull() {
+ when(mService.getNightMode()).thenReturn(UiModeManager.MODE_NIGHT_CUSTOM);
+ mController.refreshSummary(mPreference);
+ verify(mPreference).setSummary(eq(mFormat.of(null)));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
index b1c6738..8afda73 100644
--- a/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
+++ b/tests/robotests/src/com/android/settings/display/darkmode/DarkModeScheduleSelectorControllerTest.java
@@ -65,6 +65,7 @@
when(mContext.getSystemService(PowerManager.class)).thenReturn(mPM);
when(mContext.getString(R.string.dark_ui_auto_mode_never)).thenReturn("never");
when(mContext.getString(R.string.dark_ui_auto_mode_auto)).thenReturn("auto");
+ when(mContext.getString(R.string.dark_ui_auto_mode_custom)).thenReturn("custom");
mPreference = spy(new DropDownPreference(mContext));
mPreference.setEntryValues(new CharSequence[]{
mContext.getString(R.string.dark_ui_auto_mode_never),
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
index af37797..be3c685 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardManagerTest.java
@@ -17,9 +17,9 @@
package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardManager.KEY_CONTEXTUAL_CARDS;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH;
+import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_STICKY;
import static com.google.common.truth.Truth.assertThat;
@@ -110,8 +110,8 @@
mManager = new ContextualCardManager(mContext, mLifecycle, outState);
final List<String> actualCards = mManager.mSavedCards.stream().collect(Collectors.toList());
- final List<String> expectedCards = Arrays.asList("test_wifi", "test_flashlight",
- "test_connected", "test_gesture", "test_battery");
+ final List<String> expectedCards = Arrays.asList("test_low_storage", "test_flashlight",
+ "test_dark_theme", "test_gesture", "test_battery");
assertThat(actualCards).containsExactlyElementsIn(expectedCards);
}
@@ -348,8 +348,9 @@
final ConditionContextualCardController conditionController =
pool.getController(mContext,
ContextualCard.CardType.CONDITIONAL);
- final OnStart controller = spy((OnStart)conditionController);
- doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+ final OnStart controller = spy((OnStart) conditionController);
+ doReturn(controller).when(pool).getController(mContext,
+ ContextualCard.CardType.CONDITIONAL);
manager.onWindowFocusChanged(true /* hasWindowFocus */);
@@ -366,8 +367,9 @@
final ConditionContextualCardController conditionController =
pool.getController(mContext,
ContextualCard.CardType.CONDITIONAL);
- final OnStart controller = spy((OnStart)conditionController);
- doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+ final OnStart controller = spy((OnStart) conditionController);
+ doReturn(controller).when(pool).getController(mContext,
+ ContextualCard.CardType.CONDITIONAL);
manager.onWindowFocusChanged(true /* hasWindowFocus */);
@@ -385,7 +387,8 @@
pool.getController(mContext,
ContextualCard.CardType.CONDITIONAL);
final OnStop controller = spy((OnStop) conditionController);
- doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+ doReturn(controller).when(pool).getController(mContext,
+ ContextualCard.CardType.CONDITIONAL);
manager.onWindowFocusChanged(false /* hasWindowFocus */);
@@ -401,7 +404,8 @@
pool.getController(mContext,
ContextualCard.CardType.CONDITIONAL);
final OnStop controller = spy((OnStop) conditionController);
- doReturn(controller).when(pool).getController(mContext, ContextualCard.CardType.CONDITIONAL);
+ doReturn(controller).when(pool).getController(mContext,
+ ContextualCard.CardType.CONDITIONAL);
manager.onWindowFocusChanged(false /* hasWindowFocus */);
@@ -542,35 +546,65 @@
}
@Test
- public void getCardsWithViewType_onlyDeferredSetupCard_shouldHaveDeferredSetupCard() {
- final List<ContextualCard> oneDeferredSetupCards = getDeferredSetupCardList();
-
- final List<ContextualCard> result = mManager.getCardsWithViewType(oneDeferredSetupCards);
-
- assertThat(result).hasSize(1);
- assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
- }
-
- @Test
- public void getCardsWithViewType_hasDeferredSetupCard_shouldHaveDeferredSetupCard() {
+ public void getCardsWithViewType_hasWifiSlice_shouldHaveOneStickyCard() {
+ final List<ContextualCard> cards = new ArrayList<>();
+ cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString()));
+ cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
final List<Integer> categories = Arrays.asList(
- ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
- ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
- ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
);
- final List<ContextualCard> cards = buildCategoriedCards(getContextualCardList(),
- categories);
+ final List<ContextualCard> cardListWithWifi = buildCategoriedCards(cards, categories);
- final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
+ final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithWifi);
- assertThat(result).hasSize(5);
- assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_DEFERRED_SETUP);
+ assertThat(result).hasSize(cards.size());
+ assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_STICKY);
+ assertThat(result.get(1).getViewType()).isEqualTo(VIEW_TYPE_FULL_WIDTH);
}
@Test
- public void getCardsWithViewType_noDeferredSetupCard_shouldNotHaveDeferredSetupCard() {
+ public void getCardsWithViewType_hasBluetoothDeviceSlice_shouldHaveOneStickyCard() {
+ final List<ContextualCard> cards = new ArrayList<>();
+ cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()));
+ cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
+ final List<Integer> categories = Arrays.asList(
+ ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+ ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
+ );
+ final List<ContextualCard> cardListWithBT = buildCategoriedCards(cards, categories);
+
+ final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithBT);
+
+ assertThat(result).hasSize(cards.size());
+ assertThat(result.get(0).getViewType()).isEqualTo(VIEW_TYPE_STICKY);
+ assertThat(result.get(1).getViewType()).isEqualTo(VIEW_TYPE_FULL_WIDTH);
+ }
+
+ @Test
+ public void getCardsWithViewType_hasWifiAndBtDeviceSlice_shouldHaveTwoStickyCards() {
+ final List<ContextualCard> cards = new ArrayList<>();
+ cards.add(buildContextualCard(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI.toString()));
+ cards.add(buildContextualCard(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI.toString()));
+ cards.add(buildContextualCard(CustomSliceRegistry.LOW_STORAGE_SLICE_URI.toString()));
+ final List<Integer> categories = Arrays.asList(
+ ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+ ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
+ ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
+ );
+ final List<ContextualCard> cardListWithWifiBT = buildCategoriedCards(cards, categories);
+
+ final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithWifiBT);
+
+ assertThat(result).hasSize(cards.size());
+ assertThat(result.stream()
+ .filter(card -> card.getViewType() == VIEW_TYPE_STICKY)
+ .count())
+ .isEqualTo(2);
+ }
+
+ @Test
+ public void getCardsWithViewType_noWifiOrBtDeviceSlice_shouldNotHaveStickyCard() {
final List<Integer> categories = Arrays.asList(
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
ContextualCardProto.ContextualCard.Category.IMPORTANT_VALUE,
@@ -578,16 +612,16 @@
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE,
ContextualCardProto.ContextualCard.Category.SUGGESTION_VALUE
);
- final List<ContextualCard> cards = buildCategoriedCards(
- getContextualCardList(), categories);
+ final List<ContextualCard> cardListWithoutWifiBT =
+ buildCategoriedCards(getContextualCardList(), categories);
- final List<ContextualCard> result = mManager.getCardsWithViewType(cards);
+ final List<ContextualCard> result = mManager.getCardsWithViewType(cardListWithoutWifiBT);
- assertThat(result).hasSize(5);
- for (int i = 0; i < result.size(); i++) {
- assertThat(result.get(i).getViewType()).isNotEqualTo(
- ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE);
- }
+ assertThat(result).hasSize(cardListWithoutWifiBT.size());
+ assertThat(result.stream()
+ .filter(card -> card.getViewType() == VIEW_TYPE_STICKY)
+ .count())
+ .isEqualTo(0);
}
@Test
@@ -622,9 +656,9 @@
private List<ContextualCard> getContextualCardList() {
final List<ContextualCard> cards = new ArrayList<>();
cards.add(new ContextualCard.Builder()
- .setName("test_wifi")
+ .setName("test_low_storage")
.setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(CustomSliceRegistry.CONTEXTUAL_WIFI_SLICE_URI)
+ .setSliceUri(CustomSliceRegistry.LOW_STORAGE_SLICE_URI)
.setViewType(VIEW_TYPE_FULL_WIDTH)
.build());
cards.add(new ContextualCard.Builder()
@@ -635,9 +669,9 @@
.setViewType(VIEW_TYPE_FULL_WIDTH)
.build());
cards.add(new ContextualCard.Builder()
- .setName("test_connected")
+ .setName("test_dark_theme")
.setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(CustomSliceRegistry.BLUETOOTH_DEVICES_SLICE_URI)
+ .setSliceUri(CustomSliceRegistry.DARK_THEME_SLICE_URI)
.setViewType(VIEW_TYPE_FULL_WIDTH)
.build());
cards.add(new ContextualCard.Builder()
@@ -655,16 +689,4 @@
.build());
return cards;
}
-
- private List<ContextualCard> getDeferredSetupCardList() {
- final List<ContextualCard> cards = new ArrayList<>();
- cards.add(new ContextualCard.Builder()
- .setName("deferred_setup")
- .setCardType(ContextualCard.CardType.SLICE)
- .setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
- .setSliceUri(new Uri.Builder().appendPath("test_deferred_setup_path").build())
- .setViewType(VIEW_TYPE_FULL_WIDTH)
- .build());
- return cards;
- }
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
index 26d6498..987aa8a 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/ContextualCardsAdapterTest.java
@@ -16,9 +16,6 @@
package com.android.settings.homepage.contextualcards;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
-
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
@@ -39,7 +36,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
@@ -156,7 +152,7 @@
.setName("test_name")
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(TEST_SLICE_URI)
- .setViewType(VIEW_TYPE_DEFERRED_SETUP)
+ .setViewType(SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH)
.build());
cards.add(new ContextualCard.Builder()
.setName("test_name_1")
@@ -172,7 +168,7 @@
.setName(TEST_SLICE_NAME)
.setCardType(ContextualCard.CardType.SLICE)
.setSliceUri(sliceUri)
- .setViewType(VIEW_TYPE_FULL_WIDTH)
+ .setViewType(SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH)
.build();
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
index 4a23c33..0eda973 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothDevicesSliceTest.java
@@ -16,6 +16,7 @@
package com.android.settings.homepage.contextualcards.slices;
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.SliceItem.FORMAT_SLICE;
@@ -26,9 +27,9 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
@@ -37,11 +38,13 @@
import androidx.slice.SliceItem;
import androidx.slice.SliceMetadata;
import androidx.slice.SliceProvider;
+import androidx.slice.core.SliceAction;
import androidx.slice.core.SliceQuery;
import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
import com.android.settings.testutils.SliceTester;
+import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import org.junit.After;
@@ -52,6 +55,10 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
import java.util.List;
@@ -101,18 +108,105 @@
}
@Test
- public void getSlice_hasBluetoothDevices_shouldHaveBluetoothDevicesTitle() {
+ @Config(shadows = ShadowNoBluetoothAdapter.class)
+ public void getSlice_noBluetoothHardware_shouldReturnNull() {
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ assertThat(slice).isNull();
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_bluetoothOff_shouldHaveToggle() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_OFF);
+
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ assertTitleAndIcon(metadata);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).hasSize(1);
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_bluetoothOn_shouldNotHaveToggle() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
+
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ assertTitleAndIcon(metadata);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).isEmpty();
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_bluetoothTurningOff_shouldHaveToggle() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
+ final Intent intent = new Intent().putExtra(EXTRA_TOGGLE_STATE, false);
+
+ mBluetoothDevicesSlice.onNotifyChange(intent);
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).hasSize(1);
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_bluetoothTurningOn_shouldHaveToggle() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_OFF);
+ final Intent intent = new Intent().putExtra(EXTRA_TOGGLE_STATE, true);
+
+ mBluetoothDevicesSlice.onNotifyChange(intent);
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).isEmpty();
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_noBluetoothDevice_shouldHavePairNewDeviceRow() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
+ doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
+
+ final Slice slice = mBluetoothDevicesSlice.getSlice();
+
+ final List<SliceItem> sliceItems = slice.getItems();
+ SliceTester.assertAnySliceItemContainsTitle(sliceItems, mContext.getString(
+ R.string.bluetooth_pairing_pref_title));
+ }
+
+ @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
+ public void getSlice_hasBluetoothDevices_shouldNotHavePairNewDeviceRow() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
mockBluetoothDeviceList(1);
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
final Slice slice = mBluetoothDevicesSlice.getSlice();
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
- assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_devices));
+ final List<SliceItem> sliceItems = slice.getItems();
+ SliceTester.assertNoSliceItemContainsTitle(sliceItems, mContext.getString(
+ R.string.bluetooth_pairing_pref_title));
}
@Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
public void getSlice_hasBluetoothDevices_shouldMatchBluetoothMockTitle() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
mockBluetoothDeviceList(1);
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -123,7 +217,10 @@
}
@Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
public void getSlice_hasMediaBluetoothDevice_shouldBuildMediaBluetoothAction() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
mockBluetoothDeviceList(1 /* deviceCount */);
doReturn(true).when(mBluetoothDeviceList.get(0)).isConnectedA2dpDevice();
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -134,7 +231,10 @@
}
@Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
public void getSlice_noMediaBluetoothDevice_shouldNotBuildMediaBluetoothAction() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
mockBluetoothDeviceList(1 /* deviceCount */);
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -144,18 +244,10 @@
}
@Test
- public void getSlice_noBluetoothDevices_shouldHaveNoBluetoothDevicesTitle() {
- doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
-
- final Slice slice = mBluetoothDevicesSlice.getSlice();
-
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
- assertThat(metadata.getTitle()).isEqualTo(
- mContext.getString(R.string.no_bluetooth_devices));
- }
-
- @Test
+ @Config(shadows = ShadowBluetoothAdapter.class)
public void getSlice_exceedDefaultRowCount_shouldOnlyShowDefaultRows() {
+ final ShadowBluetoothAdapter adapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+ adapter.setState(BluetoothAdapter.STATE_ON);
mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -167,20 +259,6 @@
}
@Test
- public void getSlice_exceedDefaultRowCount_shouldContainDefaultCountInSubTitle() {
- mockBluetoothDeviceList(BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT + 1);
- doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
-
- final Slice slice = mBluetoothDevicesSlice.getSlice();
-
- final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
- assertThat(metadata.getSubtitle()).isEqualTo(
- mContext.getResources().getQuantityString(R.plurals.show_bluetooth_devices,
- BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT,
- BluetoothDevicesSlice.DEFAULT_EXPANDED_ROW_COUNT));
- }
-
- @Test
public void onNotifyChange_mediaDevice_shouldActivateDevice() {
mockBluetoothDeviceList(1);
doReturn(mBluetoothDeviceList).when(mBluetoothDevicesSlice).getConnectedBluetoothDevices();
@@ -201,4 +279,22 @@
mBluetoothDeviceList.add(mCachedBluetoothDevice);
}
}
+
+ private void assertTitleAndIcon(SliceMetadata metadata) {
+ assertThat(metadata.getTitle()).isEqualTo(mContext.getString(
+ R.string.bluetooth_settings_title));
+
+ final SliceAction primaryAction = metadata.getPrimaryAction();
+ final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
+ com.android.internal.R.drawable.ic_settings_bluetooth);
+ assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+ }
+
+ @Implements(BluetoothAdapter.class)
+ public static class ShadowNoBluetoothAdapter extends ShadowBluetoothAdapter {
+ @Implementation
+ protected static BluetoothAdapter getDefaultAdapter() {
+ return null;
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
index e2eb9bd..4da5c09 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
index fb04dac..70761cf 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceContextualCardRendererTest.java
@@ -16,13 +16,11 @@
package com.android.settings.homepage.contextualcards.slices;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.app.Activity;
@@ -163,15 +161,6 @@
}
@Test
- public void bindView_deferredSetupCard_shouldNotCrash() {
- final RecyclerView.ViewHolder viewHolder = getDeferredSetupViewHolder();
- final ContextualCard card = buildContextualCard(TEST_SLICE_URI);
- mRenderer.mSliceLiveDataMap.put(TEST_SLICE_URI, mSliceLiveData);
-
- mRenderer.bindView(viewHolder, card);
- }
-
- @Test
public void viewClick_keepCard_shouldShowSlice() {
final RecyclerView.ViewHolder viewHolder = getSliceViewHolder();
final View sliceView = viewHolder.itemView.findViewById(R.id.slice_view);
@@ -257,18 +246,6 @@
return mRenderer.createViewHolder(view, VIEW_TYPE_FULL_WIDTH);
}
- private RecyclerView.ViewHolder getDeferredSetupViewHolder() {
- final RecyclerView recyclerView = new RecyclerView(mActivity);
- recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
- final View view = LayoutInflater.from(mActivity)
- .inflate(VIEW_TYPE_DEFERRED_SETUP, recyclerView, false);
- final RecyclerView.ViewHolder viewHolder = spy(
- new SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder(view));
- doReturn(VIEW_TYPE_DEFERRED_SETUP).when(viewHolder).getItemViewType();
-
- return viewHolder;
- }
-
private ContextualCard buildContextualCard(Uri sliceUri) {
return new ContextualCard.Builder()
.setName("test_name")
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java
deleted file mode 100644
index 740a45b..0000000
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SliceDeferredSetupCardRendererHelperTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.homepage.contextualcards.slices;
-
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-import static com.android.settings.homepage.contextualcards.slices.SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.net.Uri;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import androidx.core.graphics.drawable.IconCompat;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.slice.Slice;
-import androidx.slice.SliceProvider;
-import androidx.slice.builders.ListBuilder;
-import androidx.slice.builders.SliceAction;
-import androidx.slice.widget.SliceLiveData;
-
-import com.android.settings.R;
-import com.android.settings.homepage.contextualcards.ContextualCard;
-import com.android.settings.intelligence.ContextualCardProto;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-
-@RunWith(RobolectricTestRunner.class)
-public class SliceDeferredSetupCardRendererHelperTest {
- private static final Uri TEST_SLICE_URI = Uri.parse("content://test/test");
- private static final CharSequence TITLE = "test_title";
- private static final CharSequence SUMMARY = "test_summary";
-
- private Activity mActivity;
- private SliceDeferredSetupCardRendererHelper mHelper;
-
- @Before
- public void setUp() {
- // Set-up specs for SliceMetadata.
- SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
- mActivity = Robolectric.buildActivity(Activity.class).create().get();
- mActivity.setTheme(R.style.Theme_Settings_Home);
- mHelper = new SliceDeferredSetupCardRendererHelper(mActivity);
- }
-
- @Test
- public void createViewHolder_shouldAlwaysReturnCustomViewHolder() {
- final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
- assertThat(viewHolder).isInstanceOf(
- DeferredSetupCardViewHolder.class);
- }
-
- @Test
- public void bindView_shouldSetTitle() {
- final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
- mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
-
- assertThat(((DeferredSetupCardViewHolder) viewHolder).title.getText()).isEqualTo(TITLE);
- }
-
- @Test
- public void bindView_shouldSetSummary() {
- final RecyclerView.ViewHolder viewHolder = getDeferredSetupCardViewHolder();
-
- mHelper.bindView(viewHolder, buildContextualCard(), buildSlice());
-
- assertThat(((DeferredSetupCardViewHolder) viewHolder).summary.getText()).isEqualTo(SUMMARY);
- }
-
- private RecyclerView.ViewHolder getDeferredSetupCardViewHolder() {
- final RecyclerView recyclerView = new RecyclerView(mActivity);
- recyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
- final View view = LayoutInflater.from(mActivity).inflate(VIEW_TYPE_DEFERRED_SETUP,
- recyclerView, false);
-
- return mHelper.createViewHolder(view);
- }
-
- private ContextualCard buildContextualCard() {
- return new ContextualCard.Builder()
- .setName("test_name")
- .setCategory(ContextualCardProto.ContextualCard.Category.DEFERRED_SETUP_VALUE)
- .setCardType(ContextualCard.CardType.SLICE)
- .setSliceUri(TEST_SLICE_URI)
- .setViewType(VIEW_TYPE_DEFERRED_SETUP)
- .build();
- }
-
- private Slice buildSlice() {
- final IconCompat icon = IconCompat.createWithResource(mActivity, R.drawable.empty_icon);
- final PendingIntent pendingIntent = PendingIntent.getActivity(
- mActivity,
- TITLE.hashCode() /* requestCode */,
- new Intent("test action"),
- 0 /* flags */);
- final SliceAction action
- = SliceAction.createDeeplink(pendingIntent, icon, ListBuilder.SMALL_IMAGE, TITLE);
- return new ListBuilder(mActivity, TEST_SLICE_URI, ListBuilder.INFINITY)
- .addRow(new ListBuilder.RowBuilder()
- .addEndItem(icon, ListBuilder.ICON_IMAGE)
- .setTitle(TITLE)
- .setSubtitle(SUMMARY)
- .setPrimaryAction(action))
- .build();
- }
-}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
index 8338ae4..3e5934b 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/SwipeDismissalDelegateTest.java
@@ -16,8 +16,6 @@
package com.android.settings.homepage.contextualcards.slices;
-import static com.android.settings.homepage.contextualcards.slices.SliceContextualCardRenderer.VIEW_TYPE_DEFERRED_SETUP;
-
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,7 +33,6 @@
import com.android.settings.R;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer;
import com.android.settings.homepage.contextualcards.conditional.ConditionContextualCardRenderer.ConditionalCardHolder;
-import com.android.settings.homepage.contextualcards.slices.SliceDeferredSetupCardRendererHelper.DeferredSetupCardViewHolder;
import com.android.settings.homepage.contextualcards.slices.SliceFullCardRendererHelper.SliceViewHolder;
import org.junit.Before;
@@ -77,12 +74,6 @@
}
@Test
- public void getMovementFlags_deferredSetupViewHolder_shouldDisableSwipe() {
- assertThat(mDismissalDelegate.getMovementFlags(mRecyclerView, getDeferredSetupViewHolder()))
- .isEqualTo(0);
- }
-
- @Test
public void getMovementFlags_dismissalView_shouldDisableSwipe() {
final RecyclerView.ViewHolder holder = getSliceViewHolder();
holder.itemView.findViewById(R.id.dismissal_view).setVisibility(View.VISIBLE);
@@ -126,13 +117,4 @@
return viewHolder;
}
-
- private RecyclerView.ViewHolder getDeferredSetupViewHolder() {
- final View view = LayoutInflater.from(mActivity)
- .inflate(VIEW_TYPE_DEFERRED_SETUP, mRecyclerView, false);
- final RecyclerView.ViewHolder viewHolder = spy(new DeferredSetupCardViewHolder(view));
- doReturn(VIEW_TYPE_DEFERRED_SETUP).when(viewHolder).getItemViewType();
-
- return viewHolder;
- }
}
diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
index 85fc9a2..5afd4c4 100644
--- a/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
+++ b/tests/robotests/src/com/android/settings/network/SubscriptionUtilTest.java
@@ -62,7 +62,7 @@
@Test
public void getAvailableSubscriptions_nullInfoFromSubscriptionManager_nonNullResult() {
- when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(null);
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(null);
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
assertThat(subs).isNotNull();
assertThat(subs).isEmpty();
@@ -71,7 +71,7 @@
@Test
public void getAvailableSubscriptions_oneSubscription_oneResult() {
final SubscriptionInfo info = mock(SubscriptionInfo.class);
- when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info));
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info));
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
assertThat(subs).isNotNull();
assertThat(subs).hasSize(1);
@@ -81,7 +81,7 @@
public void getAvailableSubscriptions_twoSubscriptions_twoResults() {
final SubscriptionInfo info1 = mock(SubscriptionInfo.class);
final SubscriptionInfo info2 = mock(SubscriptionInfo.class);
- when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2));
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2));
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(mContext);
assertThat(subs).isNotNull();
assertThat(subs).hasSize(2);
@@ -106,7 +106,7 @@
when(info3.getSimSlotIndex()).thenReturn(0);
when(info3.getCardString()).thenReturn("info3_cardid");
- when(mSubMgr.getSelectableSubscriptionInfoList()).thenReturn(Arrays.asList(info1));
+ when(mSubMgr.getAvailableSubscriptionInfoList()).thenReturn(Arrays.asList(info1));
when(mSubMgr.getAllSubscriptionInfoList()).thenReturn(Arrays.asList(info1, info2, info3));
final UiccSlotInfo info2slot = mock(UiccSlotInfo.class);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
index 714eb4e..3e0bfa3 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gBasePreferenceControllerTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -34,6 +35,7 @@
import com.android.ims.ImsManager;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
@@ -61,6 +63,8 @@
@Mock
private ProvisioningManager mProvisioningManager;
+ private VolteQueryImsState mQueryImsState;
+
private Enhanced4gLtePreferenceController mController;
private SwitchPreference mPreference;
private PersistableBundle mCarrierConfig;
@@ -81,15 +85,18 @@
mCarrierConfig = new PersistableBundle();
doReturn(mCarrierConfig).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
+ mQueryImsState = spy(new VolteQueryImsState(mContext, SUB_ID));
+
mPreference = new RestrictedSwitchPreference(mContext);
- mController = new Enhanced4gLtePreferenceController(mContext, "roaming") {
+ mController = spy(new Enhanced4gLtePreferenceController(mContext, "roaming") {
@Override
ProvisioningManager getProvisioningManager(int subId) {
return mProvisioningManager;
}
- };
+ });
mController.init(SUB_ID);
mController.mImsManager = mImsManager;
+ doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
mPreference.setKey(mController.getPreferenceKey());
}
@@ -114,6 +121,7 @@
@Test
public void updateState_configEnabled_prefEnabled() {
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
mPreference.setEnabled(false);
mCarrierConfig.putInt(CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 1);
mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
@@ -127,6 +135,7 @@
@Test
public void updateState_configOn_prefChecked() {
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
mPreference.setChecked(false);
doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
diff --git a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
index b61504a..c9abe23 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/Enhanced4gLteSliceHelperTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -31,6 +32,7 @@
import android.content.Context;
import android.content.Intent;
import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.ims.ProvisioningManager;
import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -45,6 +47,7 @@
import com.android.ims.ImsManager;
import com.android.settings.R;
+import com.android.settings.network.ims.VolteQueryImsState;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -52,6 +55,7 @@
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -59,11 +63,14 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowSubscriptionManager;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class Enhanced4gLteSliceHelperTest {
+ private static final int SUB_ID = 1;
@Mock
private CarrierConfigManager mMockCarrierConfigManager;
@@ -73,6 +80,9 @@
@Mock
private ProvisioningManager mProvisioningManager;
+ private ShadowSubscriptionManager mShadowSubscriptionManager;
+ private VolteQueryImsState mQueryImsState;
+
private Context mContext;
private FakeEnhanced4gLteSliceHelper mEnhanced4gLteSliceHelper;
private SettingsSliceProvider mProvider;
@@ -85,6 +95,10 @@
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
+ mShadowSubscriptionManager = Shadow.extract(mContext.getSystemService(
+ SubscriptionManager.class));
+ mShadowSubscriptionManager.setDefaultVoiceSubscriptionId(SUB_ID);
+
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
@@ -96,7 +110,10 @@
//setup for SliceBroadcastReceiver test
mReceiver = spy(new SliceBroadcastReceiver());
- mEnhanced4gLteSliceHelper = new FakeEnhanced4gLteSliceHelper(mContext);
+ mQueryImsState = spy(new VolteQueryImsState(mContext, SUB_ID));
+
+ mEnhanced4gLteSliceHelper = spy(new FakeEnhanced4gLteSliceHelper(mContext));
+ doReturn(mQueryImsState).when(mEnhanced4gLteSliceHelper).queryImsState(anyInt());
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -104,7 +121,7 @@
@Test
public void test_CreateEnhanced4gLteSlice_invalidSubId() {
- mEnhanced4gLteSliceHelper.setDefaultVoiceSubId(-1);
+ mShadowSubscriptionManager.setDefaultVoiceSubscriptionId(-1);
final Slice slice = mEnhanced4gLteSliceHelper.createEnhanced4gLteSlice(
CustomSliceRegistry.ENHANCED_4G_SLICE_URI);
@@ -129,7 +146,7 @@
when(mProvisioningManager.getProvisioningStatusForCapability(
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
- when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
@@ -146,7 +163,7 @@
when(mProvisioningManager.getProvisioningStatusForCapability(
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
- when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
@@ -159,12 +176,13 @@
}
@Test
+ @Ignore
public void test_SliceBroadcastReceiver_toggleOffEnhanced4gLte() {
when(mMockImsManager.isVolteEnabledByPlatform()).thenReturn(true);
when(mProvisioningManager.getProvisioningStatusForCapability(
MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
ImsRegistrationImplBase.REGISTRATION_TECH_LTE)).thenReturn(true);
- when(mMockImsManager.isEnhanced4gLteModeSettingEnabledByUser()).thenReturn(false);
+ doReturn(false).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mSlicesFeatureProvider.getNewEnhanced4gLteSliceHelper(mContext))
.thenReturn(mEnhanced4gLteSliceHelper);
diff --git a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
index 4cc8f36..ba37c30 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/MobileDataSliceTest.java
@@ -24,8 +24,8 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
@@ -79,9 +79,8 @@
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
doReturn(mSubscriptionInfo).when(mSubscriptionManager).getActiveSubscriptionInfo(anyInt());
doReturn(SUB_ID).when(mSubscriptionInfo).getSubscriptionId();
- doReturn(new ArrayList<>(Arrays.asList(mSubscriptionInfo)))
- .when(mSubscriptionManager).getSelectableSubscriptionInfoList();
-
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+ Arrays.asList(mSubscriptionInfo));
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -172,7 +171,7 @@
@Test
public void isMobileDataAvailable_noSubscriptions_slicePrimaryActionIsEmpty() {
- doReturn(new ArrayList<>()).when(mSubscriptionManager).getSelectableSubscriptionInfoList();
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(new ArrayList<>());
final Slice mobileData = mMobileDataSlice.getSlice();
assertThat(mobileData).isNull();
@@ -180,7 +179,7 @@
@Test
public void isMobileDataAvailable_nullSubscriptions_slicePrimaryActionIsEmpty() {
- doReturn(null).when(mSubscriptionManager).getSelectableSubscriptionInfoList();
+ when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(null);
final Slice mobileData = mMobileDataSlice.getSlice();
assertThat(mobileData).isNull();
diff --git a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
index 7bbca28..e68a0f4 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/VideoCallingPreferenceControllerTest.java
@@ -35,6 +35,8 @@
import androidx.preference.SwitchPreference;
import com.android.ims.ImsManager;
+import com.android.settings.network.ims.VolteQueryImsState;
+import com.android.settings.network.ims.VtQueryImsState;
import org.junit.Before;
import org.junit.Test;
@@ -59,6 +61,9 @@
@Mock
private PreferenceScreen mPreferenceScreen;
+ private VtQueryImsState mQueryImsState;
+ private VolteQueryImsState mQueryVoLteState;
+
private VideoCallingPreferenceController mController;
private PersistableBundle mCarrierConfig;
private SwitchPreference mPreference;
@@ -80,15 +85,23 @@
CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
doReturn(mCarrierConfig).when(mCarrierConfigManager).getConfigForSubId(SUB_ID);
+ mQueryImsState = spy(new VtQueryImsState(mContext, SUB_ID));
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
+
+ mQueryVoLteState = spy(new VolteQueryImsState(mContext, SUB_ID));
+ doReturn(true).when(mQueryVoLteState).isEnabledByUser();
+
mPreference = new SwitchPreference(mContext);
- mController = new VideoCallingPreferenceController(mContext, "wifi_calling") {
+ mController = spy(new VideoCallingPreferenceController(mContext, "wifi_calling") {
@Override
ProvisioningManager getProvisioningManager(int subId) {
return mProvisioningManager;
}
- };
+ });
mController.init(SUB_ID);
mController.mImsManager = mImsManager;
+ doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
+ doReturn(mQueryVoLteState).when(mController).queryVoLteState(anyInt());
mPreference.setKey(mController.getPreferenceKey());
doReturn(true).when(mImsManager).isVtEnabledByPlatform();
@@ -124,7 +137,8 @@
@Test
public void updateState_4gLteOff_disabled() {
- doReturn(false).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+ doReturn(false).when(mQueryImsState).isEnabledByUser();
+ doReturn(false).when(mQueryVoLteState).isEnabledByUser();
mController.updateState(mPreference);
@@ -134,8 +148,8 @@
@Test
public void updateState_4gLteOnWithoutCall_checked() {
- doReturn(true).when(mImsManager).isVtEnabledByUser();
- doReturn(true).when(mImsManager).isEnhanced4gLteModeSettingEnabledByUser();
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
+ doReturn(true).when(mQueryVoLteState).isEnabledByUser();
doReturn(true).when(mImsManager).isNonTtyOrTtyOnVolteEnabled();
mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
diff --git a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
index ffd0eb8..961f8cb 100644
--- a/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/telephony/WifiCallingPreferenceControllerTest.java
@@ -19,6 +19,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -38,6 +40,7 @@
import com.android.ims.ImsManager;
import com.android.internal.R;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import org.junit.Before;
import org.junit.Ignore;
@@ -63,6 +66,8 @@
@Mock
private PreferenceScreen mPreferenceScreen;
+ private WifiCallingQueryImsState mQueryImsState;
+
private WifiCallingPreferenceController mController;
private Preference mPreference;
private Context mContext;
@@ -74,6 +79,9 @@
mContext = spy(RuntimeEnvironment.application);
+ mQueryImsState = spy(new WifiCallingQueryImsState(mContext, SUB_ID));
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
+
mPreference = new Preference(mContext);
mController = spy(new WifiCallingPreferenceController(mContext, "wifi_calling") {
@Override
@@ -85,6 +93,7 @@
mController.init(SUB_ID);
mController.mImsManager = mImsManager;
mController.mCallState = TelephonyManager.CALL_STATE_IDLE;
+ doReturn(mQueryImsState).when(mController).queryImsState(anyInt());
mPreference.setKey(mController.getPreferenceKey());
when(mController.getTelephonyManager(mContext, SUB_ID)).thenReturn(mTelephonyManager);
@@ -99,7 +108,7 @@
@Test
public void updateState_noSimCallManager_setCorrectSummary() {
mController.mSimCallManager = null;
- when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mImsMmTelManager.getVoWiFiRoamingModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_WIFI_ONLY);
when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
@@ -140,7 +149,7 @@
ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
- when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mTelephonyManager.isNetworkRoaming()).thenReturn(true);
mController.updateState(mPreference);
@@ -157,7 +166,7 @@
ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
when(mImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED);
- when(mImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mTelephonyManager.isNetworkRoaming()).thenReturn(true);
mController.updateState(mPreference);
diff --git a/tests/robotests/src/com/android/settings/testutils/SliceTester.java b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
index 6fb2c49..03a7146 100644
--- a/tests/robotests/src/com/android/settings/testutils/SliceTester.java
+++ b/tests/robotests/src/com/android/settings/testutils/SliceTester.java
@@ -243,6 +243,16 @@
}
/**
+ * Assert no slice item contains title.
+ *
+ * @param sliceItems All slice items of a Slice.
+ * @param title Title for asserting.
+ */
+ public static void assertNoSliceItemContainsTitle(List<SliceItem> sliceItems, String title) {
+ assertThat(hasText(sliceItems, title, HINT_TITLE)).isFalse();
+ }
+
+ /**
* Assert any slice item contains subtitle.
*
* @param sliceItems All slice items of a Slice.
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index cf96aba..c1f33c6 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -18,9 +18,11 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.ArraySet;
import com.android.settings.Utils;
@@ -40,6 +42,7 @@
private static Map<String, String> sAppNameMap;
private static boolean sIsSystemAlertWindowEnabled;
private static boolean sIsVoiceCapable;
+ private static ArraySet<String> sResultLinks = new ArraySet<>();
@Implementation
protected static int enforceSameOwner(Context context, int userId) {
@@ -60,6 +63,7 @@
sIsUserAMonkey = false;
sIsDemoUser = false;
sIsVoiceCapable = false;
+ sResultLinks = new ArraySet<>();
}
public static void setIsDemoUser(boolean isDemoUser) {
@@ -134,4 +138,13 @@
public static void setIsVoiceCapable(boolean isVoiceCapable) {
sIsVoiceCapable = isVoiceCapable;
}
+
+ @Implementation
+ protected static ArraySet<String> getHandledDomains(PackageManager pm, String packageName) {
+ return sResultLinks;
+ }
+
+ public static void setHandledDomains(ArraySet<String> links) {
+ sResultLinks = links;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
index 17f8d36..ae236bf 100644
--- a/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/addappnetworks/AddAppNetworksFragmentTest.java
@@ -18,8 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.net.wifi.WifiConfiguration;
@@ -32,10 +35,15 @@
import androidx.annotation.NonNull;
import com.android.settings.R;
+import com.android.settingslib.wifi.AccessPoint;
+import com.android.settingslib.wifi.WifiTracker;
+import com.android.settingslib.wifi.WifiTrackerFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.shadows.androidx.fragment.FragmentController;
@@ -49,6 +57,9 @@
private static final String FAKE_NEW_OPEN_SSID = "fake_new_open_ssid";
private static final String FAKE_NEW_OPEN_SSID_WITH_QUOTE = "\"fake_new_open_ssid\"";
private static final String FAKE_NEW_SAVED_WPA_SSID = "\"fake_new_wpa_ssid\"";
+ private static final String KEY_SSID = "key_ssid";
+ private static final String KEY_SECURITY = "key_security";
+ private static final int SCANED_LEVEL = 4;
private AddAppNetworksFragment mAddAppNetworksFragment;
private List<WifiNetworkSuggestion> mFakedSpecifiedNetworksList;
@@ -59,8 +70,15 @@
private Bundle mBundle;
private ArrayList<Integer> mFakedResultArrayList = new ArrayList<>();
+ @Mock
+ private AccessPoint mMockAccessPoint;
+
+ @Mock
+ private WifiTracker mMockWifiTracker;
+
@Before
public void setUp() {
+ MockitoAnnotations.initMocks(this);
mAddAppNetworksFragment = spy(new AddAppNetworksFragment());
mNewWpaSuggestionEntry = generateRegularWifiSuggestion(FAKE_NEW_WPA_SSID,
WifiConfiguration.KeyMgmt.WPA_PSK, "1234567890");
@@ -68,6 +86,10 @@
WifiConfiguration.KeyMgmt.NONE, null);
mSavedWpaConfigurationEntry = generateRegularWifiConfiguration(FAKE_NEW_SAVED_WPA_SSID,
WifiConfiguration.KeyMgmt.WPA_PSK, "\"1234567890\"");
+ mAddAppNetworksFragment.mWifiTracker = mMockWifiTracker;
+ WifiTrackerFactory.setTestingWifiTracker(mMockWifiTracker);
+
+ setUpOneScannedNetworkWithScanedLevel();
}
@Test
@@ -138,7 +160,7 @@
assertThat(mAddAppNetworksFragment.mResultCodeArrayList.get(0)).isEqualTo(
mAddAppNetworksFragment.RESULT_NETWORK_ALREADY_EXISTS);
assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(
- 0).mWifiNetworkSuggestion.wifiConfiguration.SSID).isEqualTo(
+ 0).mWifiNetworkSuggestion.getWifiConfiguration().SSID).isEqualTo(
FAKE_NEW_OPEN_SSID_WITH_QUOTE);
}
@@ -165,6 +187,52 @@
assertThat(mAddAppNetworksFragment.mUiToRequestedList).hasSize(3);
}
+ @Test
+ public void withOneSuggestion_uiElementShouldHaveInitLevel() {
+ // Arrange
+ // Setup a fake saved network list and assign to fragment.
+ addOneSavedNetworkConfiguration(mSavedWpaConfigurationEntry);
+ // Setup one specified networks and its results and assign to fragment.
+ addOneSpecifiedRegularNetworkSuggestion(mNewOpenSuggestionEntry);
+ mAddAppNetworksFragment.mAllSpecifiedNetworksList = mFakedSpecifiedNetworksList;
+
+ // Act
+ mAddAppNetworksFragment.filterSavedNetworks(mFakeSavedNetworksList);
+
+ // Assert
+ assertThat(mAddAppNetworksFragment.mUiToRequestedList).hasSize(1);
+ assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(0).mLevel).isEqualTo(
+ mAddAppNetworksFragment.INITIAL_RSSI_SIGNAL_LEVEL);
+ }
+
+ @Test
+ public void withOneSuggestion_whenScanResultChanged_uiListShouldHaveNewLevel() {
+ // Arrange
+ // Setup a fake saved network list and assign to fragment.
+ addOneSavedNetworkConfiguration(mSavedWpaConfigurationEntry);
+ // Setup one specified networks and its results and assign to fragment.
+ addOneSpecifiedRegularNetworkSuggestion(mNewOpenSuggestionEntry);
+ mAddAppNetworksFragment.mAllSpecifiedNetworksList = mFakedSpecifiedNetworksList;
+ // Call filterSavedNetworks to generate necessary objects.
+ mAddAppNetworksFragment.filterSavedNetworks(mFakeSavedNetworksList);
+
+ // Act
+ mAddAppNetworksFragment.onAccessPointsChanged();
+
+ // Assert
+ assertThat(mAddAppNetworksFragment.mUiToRequestedList.get(0).mLevel).isEqualTo(
+ SCANED_LEVEL);
+ }
+
+ private void setUpOneScannedNetworkWithScanedLevel() {
+ final ArrayList list = new ArrayList<>();
+ list.add(mMockAccessPoint);
+ when(mMockWifiTracker.getAccessPoints()).thenReturn(list);
+ when(mMockAccessPoint.getSsidStr()).thenReturn(FAKE_NEW_OPEN_SSID);
+ when(mMockAccessPoint.matches(any(WifiConfiguration.class))).thenReturn(true);
+ when(mMockAccessPoint.getLevel()).thenReturn(SCANED_LEVEL);
+ }
+
private void addOneSavedNetworkConfiguration(@NonNull WifiConfiguration wifiConfiguration) {
if (mFakeSavedNetworksList == null) {
mFakeSavedNetworksList = new ArrayList<>();
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
index eb9a58d..5b76adc 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSettingsForSubTest.java
@@ -44,7 +44,6 @@
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ProvisioningManager;
import android.view.View;
import android.widget.TextView;
@@ -60,6 +59,7 @@
import com.android.settings.widget.ToggleSwitch;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -189,14 +189,15 @@
public void onResumeOnPause_provisioningCallbackRegistration() throws Exception {
// Verify that provisioning callback is registered after call to onResume().
mFragment.onResume();
- verify(mImsConfig).addConfigCallback(any(ProvisioningManager.Callback.class));
+ verify(mFragment).registerProvisioningChangedCallback();
// Verify that provisioning callback is unregistered after call to onPause.
mFragment.onPause();
- verify(mImsConfig).removeConfigCallback(any());
+ verify(mFragment).unregisterProvisioningChangedCallback();
}
@Test
+ @Ignore
public void onResume_useWfcHomeModeConfigFalseAndEditable_shouldShowWfcRoaming() {
// Call onResume to update the WFC roaming preference.
mFragment.onResume();
@@ -327,7 +328,7 @@
verify(mPreferenceScreen).addPreference(mButtonWfcRoamingMode);
verify(mPreferenceScreen).addPreference(mUpdateAddress);
// Check the WFC enable request.
- verify(mImsManager).setWfcSetting(true);
+ verify(mImsMmTelManager).setVoWiFiSettingEnabled(true);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
index 274ce34..aaff22a 100644
--- a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java
@@ -47,6 +47,7 @@
import com.android.ims.ImsManager;
import com.android.settings.R;
+import com.android.settings.network.ims.WifiCallingQueryImsState;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -68,6 +69,7 @@
@RunWith(RobolectricTestRunner.class)
public class WifiCallingSliceHelperTest {
+ private static final int SUB_ID = 1;
private Context mContext;
@Mock
@@ -79,6 +81,8 @@
@Mock
private ImsMmTelManager mMockImsMmTelManager;
+ private WifiCallingQueryImsState mQueryImsState;
+
private FakeWifiCallingSliceHelper mWfcSliceHelper;
private SettingsSliceProvider mProvider;
private SliceBroadcastReceiver mReceiver;
@@ -101,7 +105,11 @@
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
+ mQueryImsState = spy(new WifiCallingQueryImsState(mContext, SUB_ID));
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
+
mWfcSliceHelper = spy(new FakeWifiCallingSliceHelper(mContext));
+ doReturn(mQueryImsState).when(mWfcSliceHelper).queryImsState(anyInt());
// Set-up specs for SliceMetadata.
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
@@ -137,7 +145,7 @@
*/
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+ doReturn(false).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(false);
when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent
@@ -155,7 +163,7 @@
public void test_CreateWifiCallingSlice_success() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
@@ -170,7 +178,7 @@
public void test_SettingSliceProvider_getsRightSliceWifiCalling() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockCarrierConfigManager.getConfigForSubId(1)).thenReturn(null);
when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext))
@@ -186,7 +194,7 @@
public void test_SliceBroadcastReceiver_toggleOnWifiCalling() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+ doReturn(false).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mSlicesFeatureProvider.getNewWifiCallingSliceHelper(mContext))
.thenReturn(mWfcSliceHelper);
@@ -201,7 +209,7 @@
// change the setting
mReceiver.onReceive(mContext, intent);
- verify((mMockImsManager)).setWfcSetting(mWfcSettingCaptor.capture());
+ verify((mMockImsMmTelManager)).setVoWiFiSettingEnabled(mWfcSettingCaptor.capture());
// assert the change
assertThat(mWfcSettingCaptor.getValue()).isTrue();
@@ -211,7 +219,7 @@
public void test_CreateWifiCallingPreferenceSlice_prefNotEditable() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
mWfcSliceHelper.setIsWifiCallingPrefEditable(false);
@@ -226,7 +234,7 @@
public void test_CreateWifiCallingPreferenceSlice_wfcOff() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(false);
+ doReturn(false).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
mWfcSliceHelper.setIsWifiCallingPrefEditable(true);
@@ -243,7 +251,7 @@
public void test_CreateWifiCallingPreferenceSlice_success() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -261,7 +269,7 @@
public void test_SettingsSliceProvider_getWfcPreferenceSlice() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -280,7 +288,7 @@
public void test_SliceBroadcastReceiver_setWfcPrefCellularPref() {
when(mMockImsManager.isWfcEnabledByPlatform()).thenReturn(true);
when(mWfcSliceHelper.isWfcProvisionedOnDevice(anyInt())).thenReturn(true);
- when(mMockImsManager.isWfcEnabledByUser()).thenReturn(true);
+ doReturn(true).when(mQueryImsState).isEnabledByUser();
when(mMockImsManager.isNonTtyOrTtyOnVolteEnabled()).thenReturn(true);
when(mMockImsMmTelManager.getVoWiFiModeSetting()).thenReturn(
ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED);
@@ -455,6 +463,10 @@
return true;
}
+ WifiCallingQueryImsState queryImsState(int subId) {
+ return super.queryImsState(subId);
+ }
+
@Override
protected Intent getWifiCallingCarrierActivityIntent(int subId) {
return mActivationAppIntent;
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
index d39228b..6593ec3 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/ContextualWifiSliceTest.java
@@ -16,6 +16,9 @@
package com.android.settings.wifi.slice;
+import static com.android.settings.wifi.slice.ContextualWifiSlice.COLLAPSED_ROW_COUNT;
+import static com.android.settings.wifi.slice.WifiSlice.DEFAULT_EXPANDED_ROW_COUNT;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doReturn;
@@ -75,75 +78,87 @@
mWifiManager.setWifiEnabled(true);
mWifiSlice = new ContextualWifiSlice(mContext);
- mWifiSlice.sPreviouslyDisplayed = false;
}
@Test
- public void getWifiSlice_hasActiveConnection_shouldReturnNull() {
- mWifiSlice.sPreviouslyDisplayed = false;
- connectToWifi(makeValidatedNetworkCapabilities());
-
- final Slice wifiSlice = mWifiSlice.getSlice();
-
- assertThat(wifiSlice).isNull();
- }
-
- @Test
- public void getWifiSlice_newSession_hasActiveConnection_shouldReturnNull() {
- // Session: use a non-active value
- // previous displayed: yes
- mWifiSlice.sPreviouslyDisplayed = true;
+ public void getWifiSlice_newSession_hasActiveConnection_shouldCollapseSlice() {
mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
connectToWifi(makeValidatedNetworkCapabilities());
final Slice wifiSlice = mWifiSlice.getSlice();
- assertThat(wifiSlice).isNull();
+ assertTitleAndIcon(wifiSlice);
+ assertNoToggle(wifiSlice);
+ assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
}
@Test
- public void getWifiSlice_previousDisplayed_hasActiveConnection_shouldHaveTitleAndToggle() {
+ public void getWifiSlice_newSession_noConnection_shouldExpandSlice() {
+ mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+
+ final Slice wifiSlice = mWifiSlice.getSlice();
+
+ assertTitleAndIcon(wifiSlice);
+ assertToggle(wifiSlice);
+ assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
+ }
+
+ @Test
+ public void getWifiSlice_previousExpanded_hasActiveConnection_shouldExpandSlice() {
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sPreviouslyDisplayed = true;
+ mWifiSlice.sToggleNeeded = true;
connectToWifi(makeValidatedNetworkCapabilities());
final Slice wifiSlice = mWifiSlice.getSlice();
- final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice);
- assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
-
- final List<SliceAction> toggles = metadata.getToggles();
- assertThat(toggles).hasSize(1);
-
- final SliceAction primaryAction = metadata.getPrimaryAction();
- final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
- R.drawable.ic_settings_wireless);
- assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+ assertTitleAndIcon(wifiSlice);
+ assertToggle(wifiSlice);
+ assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(DEFAULT_EXPANDED_ROW_COUNT);
}
@Test
- public void getWifiSlice_isCaptivePortal_shouldHaveTitleAndToggle() {
- mWifiSlice.sPreviouslyDisplayed = false;
- connectToWifi(WifiSliceTest.makeCaptivePortalNetworkCapabilities());
+ public void getWifiSlice_previousExpanded_disableWifi_shouldHaveToggle() {
+ mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mWifiSlice.sToggleNeeded = true;
+ connectToWifi(makeValidatedNetworkCapabilities());
+ mWifiManager.setWifiEnabled(false);
final Slice wifiSlice = mWifiSlice.getSlice();
- final SliceMetadata metadata = SliceMetadata.from(mContext, wifiSlice);
- assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
+ assertTitleAndIcon(wifiSlice);
+ assertToggle(wifiSlice);
+ }
- final List<SliceAction> toggles = metadata.getToggles();
- assertThat(toggles).hasSize(1);
+ @Test
+ public void getWifiSlice_previousCollapsed_disableWifi_shouldHaveToggle() {
+ mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mWifiSlice.sToggleNeeded = false;
+ connectToWifi(makeValidatedNetworkCapabilities());
- final SliceAction primaryAction = metadata.getPrimaryAction();
- final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
- R.drawable.ic_settings_wireless);
- assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+ mWifiManager.setWifiEnabled(false);
+ final Slice wifiSlice = mWifiSlice.getSlice();
+
+ assertTitleAndIcon(wifiSlice);
+ assertToggle(wifiSlice);
+ }
+
+ @Test
+ public void getWifiSlice_previousCollapsed_connectionLoss_shouldCollapseSlice() {
+ mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
+ mWifiSlice.sToggleNeeded = false;
+ connectToWifi(makeValidatedNetworkCapabilities());
+
+ mWifiManager.disconnect();
+ final Slice wifiSlice = mWifiSlice.getSlice();
+
+ assertTitleAndIcon(wifiSlice);
+ assertNoToggle(wifiSlice);
+ assertThat(ContextualWifiSlice.getApRowCount()).isEqualTo(COLLAPSED_ROW_COUNT);
}
@Test
public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() {
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
- mWifiSlice.sPreviouslyDisplayed = true;
final Slice wifiSlice = mWifiSlice.getSlice();
@@ -165,4 +180,26 @@
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
return nc;
}
+
+ private void assertTitleAndIcon(Slice slice) {
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ assertThat(metadata.getTitle()).isEqualTo(mContext.getString(R.string.wifi_settings));
+
+ final SliceAction primaryAction = metadata.getPrimaryAction();
+ final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
+ R.drawable.ic_settings_wireless);
+ assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
+ }
+
+ private void assertToggle(Slice slice) {
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).hasSize(1);
+ }
+
+ private void assertNoToggle(Slice slice) {
+ final SliceMetadata metadata = SliceMetadata.from(mContext, slice);
+ final List<SliceAction> toggles = metadata.getToggles();
+ assertThat(toggles).isEmpty();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
index 18ed006..ad74a32 100644
--- a/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/slice/WifiSliceTest.java
@@ -51,9 +51,6 @@
import com.android.settings.testutils.SliceTester;
import com.android.settingslib.wifi.AccessPoint;
-import java.util.ArrayList;
-import java.util.List;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -63,12 +60,16 @@
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
@Config(shadows = WifiSliceTest.ShadowSliceBackgroundWorker.class)
public class WifiSliceTest {
private static final String AP1_NAME = "ap1";
private static final String AP2_NAME = "ap2";
+ private static final String AP3_NAME = "ap3";
private Context mContext;
private ContentResolver mResolver;
@@ -205,15 +206,15 @@
}
@Test
- public void getWifiSlice_oneReachableAp_shouldNotReturnLoadingRow() {
+ public void getWifiSlice_oneReachableAp_shouldReturnLoadingRow() {
setWorkerResults(createAccessPoint(AP1_NAME, false, true));
final Slice wifiSlice = mWifiSlice.getSlice();
final List<SliceItem> sliceItems = wifiSlice.getItems();
SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
- // No scanning text
- SliceTester.assertNoSliceItemContainsSubtitle(sliceItems,
+ // Has scanning text
+ SliceTester.assertAnySliceItemContainsSubtitle(sliceItems,
mContext.getString(R.string.wifi_empty_list_wifi_on));
}
@@ -221,13 +222,15 @@
public void getWifiSlice_allReachableAps_shouldNotReturnLoadingRow() {
setWorkerResults(
createAccessPoint(AP1_NAME, false, true),
- createAccessPoint(AP2_NAME, false, true));
+ createAccessPoint(AP2_NAME, false, true),
+ createAccessPoint(AP3_NAME, false, true));
final Slice wifiSlice = mWifiSlice.getSlice();
final List<SliceItem> sliceItems = wifiSlice.getItems();
SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP1_NAME);
SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP2_NAME);
+ SliceTester.assertAnySliceItemContainsTitle(sliceItems, AP3_NAME);
// No scanning text
SliceTester.assertNoSliceItemContainsSubtitle(sliceItems,
mContext.getString(R.string.wifi_empty_list_wifi_on));