Snap for 5381581 from 47e8fbbcee to qt-release

Change-Id: If8c8c99a59453205461f68b4ad741481bda9fdc9
This commit is contained in:
android-build-team Robot
2019-03-16 23:17:27 +00:00
91 changed files with 2830 additions and 435 deletions

View File

@@ -3124,6 +3124,18 @@
<activity android:name=".homepage.contextualcards.ContextualCardFeedbackDialog"
android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
<activity
android:name="Settings$WifiCallingDisclaimerActivity"
android:label="@string/wifi_calling_settings_title"
android:taskAffinity="com.android.settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.wifi.calling.WifiCallingDisclaimerFragment" />
</activity>
<!-- This is the longest AndroidManifest.xml ever. -->
</application>
</manifest>

View File

@@ -1,4 +1,4 @@
<!--
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2018 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,10 +15,17 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="32dp"
android:width="32dp"
android:height="32dp"
android:tint="?android:attr/colorPrimary"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M9,11.75A1.25,1.25 0 0,0 7.75,13A1.25,1.25 0 0,0 9,14.25A1.25,1.25 0 0,0 10.25,13A1.25,1.25 0 0,0 9,11.75M15,11.75A1.25,1.25 0 0,0 13.75,13A1.25,1.25 0 0,0 15,14.25A1.25,1.25 0 0,0 16.25,13A1.25,1.25 0 0,0 15,11.75M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,11.71 4,11.42 4.05,11.14C6.41,10.09 8.28,8.16 9.26,5.77C11.07,8.33 14.05,10 17.42,10C18.2,10 18.95,9.91 19.67,9.74C19.88,10.45 20,11.21 20,12C20,16.41 16.41,20 12,20Z" />
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#000000"
android:pathData="M 12 13 C 13.1045694997 13 14 13.8954305003 14 15 C 14 16.1045694997 13.1045694997 17 12 17 C 10.8954305003 17 10 16.1045694997 10 15 C 10 13.8954305003 10.8954305003 13 12 13 Z" />
<path
android:fillColor="#000000"
android:pathData="M18.5,1C16.01,1,14,3.01,14,5.5V8H6c-1.1,0-2,0.9-2,2v10c0,1.1,0.9,2,2,2h12c1.1,0,2-0.9,2-2V10c0-1.1-0.9-2-2-2h-2V5.5 C16,4.12,17.12,3,18.5,3C19.88,3,21,4.12,21,5.5V6h2V5.5C23,3.01,20.99,1,18.5,1z M18,10v10H6V10H18z" />
<path android:pathData="M0,0h24v24H0V0z" />
</vector>

View File

@@ -0,0 +1,43 @@
<?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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/back">
<shape android:shape="oval">
<solid
android:color="@android:color/transparent" />
<size
android:height="48dp"
android:width="48dp"/>
<stroke android:width="1dp"
android:color="@color/notification_alert_color"/>
</shape>
</item>
<item
android:id="@+id/fore"
android:gravity="center">
<vector
android:height="24dp"
android:width="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@color/notification_alert_color"
android:pathData="M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z"/>
</vector>
</item>
</layer-list>

View File

@@ -0,0 +1,43 @@
<?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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/back">
<shape android:shape="oval">
<solid
android:color="@android:color/transparent" />
<size
android:height="48dp"
android:width="48dp"/>
<stroke android:width="1dp"
android:color="@color/notification_block_color"/>
</shape>
</item>
<item
android:id="@+id/fore"
android:gravity="center">
<vector
android:height="24dp"
android:width="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@color/notification_block_color"
android:pathData="M12.0,2.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0zM4.0,12.0c0.0,-4.42 3.58,-8.0 8.0,-8.0 1.85,0.0 3.5,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4.0,13.85 4.0,12.0zm8.0,8.0c-1.85,0.0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20.0,10.15 20.0,12.0c0.0,4.42 -3.58,8.0 -8.0,8.0z"/>
</vector>
</item>
</layer-list>

View File

@@ -0,0 +1,43 @@
<?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.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/back">
<shape android:shape="oval">
<solid
android:color="@android:color/transparent" />
<size
android:height="48dp"
android:width="48dp"/>
<stroke android:width="1dp"
android:color="@color/notification_silence_color"/>
</shape>
</item>
<item
android:id="@+id/fore"
android:gravity="center">
<vector
android:height="24dp"
android:width="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="@color/notification_silence_color"
android:pathData="M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z" />
</vector>
</item>
</layer-list>

View File

@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_entities_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/block"
android:layout_width="0dp"
android:layout_weight="33.33"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:gravity="center">
<ImageButton
android:id="@+id/block_icon"
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_notification_block"
android:contentDescription="@string/notification_block_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notification_block_title"
android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
</LinearLayout>
<LinearLayout
android:id="@+id/silence"
android:layout_width="0dp"
android:layout_weight="33.33"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:gravity="center">
<ImageButton
android:id="@+id/silence_icon"
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_notification_silence"
android:contentDescription="@string/notification_silence_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notification_silence_title"
android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
</LinearLayout>
<LinearLayout
android:id="@+id/alert"
android:layout_width="0dp"
android:layout_weight="33.33"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
android:orientation="vertical"
android:gravity="center">
<ImageButton
android:id="@+id/alert_icon"
android:layout_width="@dimen/notification_importance_toggle_size"
android:layout_height="@dimen/notification_importance_toggle_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_notification_alert"
android:contentDescription="@string/notification_alert_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/notification_alert_title"
android:layout_marginTop="@dimen/notification_importance_toggle_marginTop"
android:layout_marginBottom="@dimen/notification_importance_toggle_marginBottom"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="16dp"
android:layout_marginBottom="20dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/wfc_disclaimer_title_text" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/disclaimer_item_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<Button
android:id="@+id/disagree_button"
style="@style/DisclaimerNegativeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/wfc_disclaimer_disagree_text" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:id="@+id/agree_button"
style="@style/DisclaimerPositiveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/wfc_disclaimer_agree_button_text" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="20dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/disclaimer_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Material.Subhead" />
<TextView
android:id="@+id/disclaimer_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary"
android:layout_below="@id/disclaimer_title" />
</RelativeLayout>

View File

@@ -56,7 +56,7 @@
<color name="material_blue_700">#3367D6</color>
<color name="material_grey_100">#f5f5f5</color>
<color name="material_grey_200">#ffffff</color>
<color name="switch_bar_background">#ff80868B</color>
<color name="switch_bar_background">#757575</color>
<color name="message_text_incoming">#ffffffff</color>
<color name="message_text_outgoing">#ff323232</color>
@@ -124,6 +124,11 @@
<color name="face_anim_particle_color_4">#fffdd835</color> <!-- Material Yellow 600 -->
<color name="face_anim_particle_error">#ff9e9e9e</color> <!-- Material Gray 500 -->
<!-- notification settings -->
<color name="notification_block_color">#ffff0000</color>
<color name="notification_silence_color">#fbbc04</color>
<color name="notification_alert_color">#30a751</color>
<!-- launcher icon color -->
<color name="icon_launcher_setting_color">@*android:color/accent_device_default_light</color>

View File

@@ -68,6 +68,9 @@
<dimen name="notification_app_icon_size">64dp</dimen>
<dimen name="notification_app_icon_badge_size">20dp</dimen>
<dimen name="notification_app_icon_badge_margin">4dp</dimen>
<dimen name="notification_importance_toggle_size">48dp</dimen>
<dimen name="notification_importance_toggle_marginTop">8dp</dimen>
<dimen name="notification_importance_toggle_marginBottom">16dp</dimen>
<dimen name="zen_schedule_rule_checkbox_padding">7dp</dimen>
<dimen name="zen_schedule_day_margin">17dp</dimen>

View File

@@ -3022,14 +3022,14 @@
<string name="status_prl_version">PRL version</string>
<!-- About phone screen, title for MEID for multi-sim devices -->
<string name="meid_multi_sim">MEID (sim slot %1$d)</string>
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are on. [CHAR LIMIT=120] -->
<string name="scanning_status_text_wifi_on_ble_on">Both Wi\u2011Fi and Bluetooth are allowed to determine location</string>
<!-- The status text when Wi-Fi scanning is on and Bluetooth scanning are off. [CHAR LIMIT=120] -->
<string name="scanning_status_text_wifi_on_ble_off">Only Wi\u2011Fi is allowed to determine location</string>
<!-- The status text when Wi-Fi scanning is off and Bluetooth scanning are on. [CHAR LIMIT=120] -->
<string name="scanning_status_text_wifi_off_ble_on">Only Bluetooth is allowed to determine location</string>
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are off. [CHAR LIMIT=120] -->
<string name="scanning_status_text_wifi_off_ble_off">Neither Wi\u2011Fi nor Bluetooth is allowed to determine location</string>
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are on. [CHAR LIMIT=100] -->
<string name="scanning_status_text_wifi_on_ble_on">Both Wi\u2011Fi and Bluetooth scanning are on</string>
<!-- The status text when Wi-Fi scanning is on and Bluetooth scanning are off. [CHAR LIMIT=100] -->
<string name="scanning_status_text_wifi_on_ble_off">Wi\u2011Fi scanning is on, Bluetooth scanning is off</string>
<!-- The status text when Wi-Fi scanning is off and Bluetooth scanning are on. [CHAR LIMIT=100] -->
<string name="scanning_status_text_wifi_off_ble_on">Bluetooth scanning is on, Wi\u2011Fi scanning is off</string>
<!-- The status text when both Wi-Fi scanning and Bluetooth scanning are off. [CHAR LIMIT=100] -->
<string name="scanning_status_text_wifi_off_ble_off">Both Wi\u2011Fi and Bluetooth scanning are off</string>
<!-- About phone, status item title. The phone MEID number of the current LTE/CDMA device. [CHAR LIMIT=30] -->
<string name="status_meid_number">MEID</string>
<!-- About phone, status item title. The ICCID of the current LTE device. [CHAR LIMIT=30] -->
@@ -3754,13 +3754,25 @@
<string name="location_app_level_permissions">App permission</string>
<!-- Summary for app permission on Location settings page when location is off [CHAR LIMIT=NONE] -->
<string name="location_app_permission_summary_location_off">Location is off</string>
<!-- Summary for Location settings when location is on, explaining how many apps have location permission [CHAR LIMIT=NONE]-->
<!--
Summary for Location settings when location is on, explaining how many apps have unlimited
location permission.
"Unlimited access" means the app can access the device location even when it's not being used
(on background), while "limited" means the app can only access the device location when the user
is using it (foreground only).
Please note that the distinction between singular and plural of this sentence only depends on
the quantity of "background_location_app_count" ("has" vs "have"). The quantity of
"total_location_app_count" is almost always greater than 1, so "apps" is always in plural form.
[CHAR LIMIT=NONE]-->
<plurals name="location_app_permission_summary_location_on">
<item quantity="one">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
<xliff:g id="total_location_app_count">%2$d</xliff:g>
app has unlimited access</item>
apps has unlimited access</item>
<item quantity="other">
<xliff:g id="background_location_app_count">%1$d</xliff:g>
of
@@ -3771,8 +3783,12 @@
<string name="location_category_recent_location_access">Recent location access</string>
<!-- [CHAR LIMIT=30] Location settings screen, button to bring the user to view the details of recent location access -->
<string name="location_recent_location_access_view_details">View details</string>
<!-- Location settings screen, displayed when there's no recent app accessing location -->
<!-- Location settings screen, displayed when there's no recent app accessing location
(for TV) [CHAR LIMIT=100] -->
<string name="location_no_recent_apps">No apps have requested location recently</string>
<!-- Location settings screen, displayed when there's no recent app accessing location
(for phones and tablets) [CHAR LIMIT=100] -->
<string name="location_no_recent_accesses">No apps recently accessed location</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests high battery use-->
<string name="location_high_battery_use">High battery use</string>
<!-- [CHAR LIMIT=30] Location settings screen, recent location requests low battery use-->
@@ -7771,7 +7787,7 @@
summary on the channel page-->
<!-- [CHAR LIMIT=100] Notification Importance title: min importance level title -->
<string name="notification_importance_min_title">Low</string>
<string name="notification_importance_min_title">Minimize</string>
<!-- [CHAR LIMIT=100] Notification Importance title: low importance level title -->
<string name="notification_importance_low_title">Medium</string>
@@ -7780,7 +7796,16 @@
<string name="notification_importance_default_title">High</string>
<!-- [CHAR LIMIT=100] Notification Importance title: high importance level title -->
<string name="notification_importance_high_title">Urgent</string>
<string name="notification_importance_high_title">Pop on screen</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_block_title">Block</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_silence_title">Show silently</string>
<!-- [CHAR LIMIT=100] Notification Importance title -->
<string name="notification_alert_title">Alert</string>
<!-- [CHAR LIMIT=40] Notification importance title. This setting controls how notifications in older apps may alert the user (eg, sound, visual, vibrate). -->
<string name="allow_interruption">Allow interruptions</string>
@@ -10758,8 +10783,17 @@
<!-- Summary for represent which device is playing media [CHAR LIMIT=NONE] -->
<string name="media_output_panel_summary_of_playing_device">Currently playing on <xliff:g id="device_name" example="Bose headphone">%1$s</xliff:g></string>
<!-- Label for the title on wfc disclaimer fragment. [CHAR LIMIT=40] -->
<string name="wfc_disclaimer_title_text">Important information</string>
<!-- Label for the agree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
<string name="wfc_disclaimer_agree_button_text">CONTINUE</string>
<!-- Label for the disagree button on wfc disclaimer fragment. [CHAR LIMIT=30] -->
<string name="wfc_disclaimer_disagree_text">NO THANKS</string>
<!-- Message for forget passpoint dialog [CHAR LIMIT=none] -->
<string name="forget_passpoint_dialog_message">Your subscription with this provider may be cancelled. Recurring subscriptions will not be cancelled. For more information, check with your provider.</string>
<string name="forget_passpoint_dialog_message">You may lose access to any remaining time or data. Check with your provider before removing.</string>
<!-- Keywords for Content Capture feature [CHAR_LIMIT=32] -->
<string name="keywords_content_capture">content capture</string>
@@ -10767,5 +10801,4 @@
<string name="content_capture">Content Capture</string>
<!-- Description of the 'Content Capture' feature toggle in the Settings -> Privacy screen [CHAR LIMIT=NONE]-->
<string name="content_capture_summary">Allow Android to save information seen on your screen or heard in video or audio content. Android makes helpful suggestions based on your device activity.</string>
</resources>

View File

@@ -520,4 +520,17 @@
<!-- Padding between content and the start icon is 8dp. -->
<item name="contentStartPadding">8dp</item>
</style>
<style name="DisclaimerPositiveButton" parent="@style/SudGlifButton.Primary">
<item name="android:layout_margin">16dp</item>
<item name="android:paddingStart">8dp</item>
<item name="android:paddingEnd">8dp</item>
</style>
<style name="DisclaimerNegativeButton" parent="@style/SudGlifButton.Secondary">
<item name="android:layout_margin">16dp</item>
<item name="android:paddingStart">8dp</item>
<item name="android:paddingEnd">8dp</item>
</style>
</resources>

View File

@@ -61,7 +61,7 @@
android:order="1001"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<com.android.settingslib.RestrictedSwitchPreference
android:key="bubble"
android:key="bubble_pref"
android:title="@string/notification_bubbles_title"
android:order="1002"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />

View File

@@ -25,11 +25,6 @@
android:order="1"
android:layout="@layout/settings_entity_header" />
<com.android.settingslib.widget.LayoutPreference
android:key="block"
android:order="2"
android:layout="@layout/styled_switch_bar" />
<!-- Importance toggle -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="allow_sound"
@@ -38,10 +33,23 @@
android:summary="@string/allow_interruption_summary" />
<!-- Importance -->
<com.android.settings.RestrictedListPreference
<com.android.settings.notification.ImportancePreference
android:key="importance"
android:order="10"
android:title="@string/notification_importance_title" />
android:order="4"
android:title="@string/notification_importance_title"
settings:allowDividerBelow="true"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="min_importance"
android:order="5"
settings:allowDividerAbove="true"
android:title="@string/notification_importance_min_title"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="high_importance"
android:order="6"
settings:allowDividerAbove="true"
android:title="@string/notification_importance_high_title"/>
<PreferenceCategory
android:key="channel_advanced"
@@ -87,7 +95,7 @@
settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="bubble"
android:key="bubble_pref"
android:title="@string/notification_bubbles_title"
android:order="16"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
@@ -113,6 +121,7 @@
<com.android.settings.notification.NotificationFooterPreference
android:key="block_desc"
android:order="110"/>
android:order="110"
settings:allowDividerAbove="false"/>
</PreferenceScreen>

View File

@@ -16,11 +16,13 @@
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="mobile_network_list_screen"
android:title="@string/network_settings_title">
<Preference
android:key="add_more"
settings:isPreferenceVisible="false"
android:title="@string/mobile_network_list_add_more"
android:icon="@drawable/ic_menu_add"
android:order="100" >

View File

@@ -26,6 +26,11 @@
android:order="-10000"
settings:allowDividerBelow="true"/>
<com.android.settings.datausage.DataUsageSummaryPreference
android:key="status_header"
android:selectable="false"
settings:isPreferenceVisible="false"/>
<!-- Buttons -->
<com.android.settingslib.widget.ActionButtonsPreference
android:key="buttons"

View File

@@ -457,6 +457,13 @@ public class RadioInfo extends Activity {
imsWfcProvisionedSwitch = (Switch) findViewById(R.id.wfc_provisioned_switch);
eabProvisionedSwitch = (Switch) findViewById(R.id.eab_provisioned_switch);
if (!ImsManager.isImsSupportedOnDevice(phone.getContext())) {
imsVolteProvisionedSwitch.setVisibility(View.GONE);
imsVtProvisionedSwitch.setVisibility(View.GONE);
imsWfcProvisionedSwitch.setVisibility(View.GONE);
eabProvisionedSwitch.setVisibility(View.GONE);
}
cbrsDataSwitch = (Switch) findViewById(R.id.cbrs_data_switch);
cbrsDataSwitch.setVisibility(isCbrsSupported() ? View.VISIBLE : View.GONE);
@@ -631,8 +638,10 @@ public class RadioInfo extends Activity {
R.string.radioInfo_menu_viewFDN).setOnMenuItemClickListener(mViewFDNCallback);
menu.add(1, MENU_ITEM_VIEW_SDN, 0,
R.string.radioInfo_menu_viewSDN).setOnMenuItemClickListener(mViewSDNCallback);
menu.add(1, MENU_ITEM_GET_IMS_STATUS,
0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
if (ImsManager.isImsSupportedOnDevice(phone.getContext())) {
menu.add(1, MENU_ITEM_GET_IMS_STATUS,
0, R.string.radioInfo_menu_getIMS).setOnMenuItemClickListener(mGetImsStatus);
}
menu.add(1, MENU_ITEM_TOGGLE_DATA,
0, R.string.radio_info_data_connection_disable).setOnMenuItemClickListener(mToggleData);
return true;
@@ -1384,6 +1393,9 @@ public class RadioInfo extends Activity {
}
private void updateImsProvisionedState() {
if (!ImsManager.isImsSupportedOnDevice(phone.getContext())) {
return;
}
log("updateImsProvisionedState isImsVolteProvisioned()=" + isImsVolteProvisioned());
//delightful hack to prevent on-checked-changed calls from
//actually forcing the ims provisioning to its transient/current value.

View File

@@ -157,20 +157,9 @@ public class ResetNetworkConfirm extends InstrumentedFragment {
SubscriptionManager.getPhoneId(mSubId)).factoryReset();
restoreDefaultApn(context);
esimFactoryReset(context, context.getPackageName());
// There has been issues when Sms raw table somehow stores orphan
// fragments. They lead to garbled message when new fragments come
// in and combied with those stale ones. In case this happens again,
// user can reset all network settings which will clean up this table.
cleanUpSmsRawTable(context);
}
};
private void cleanUpSmsRawTable(Context context) {
ContentResolver resolver = context.getContentResolver();
Uri uri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw/permanentDelete");
resolver.delete(uri, null, null);
}
@VisibleForTesting
void esimFactoryReset(Context context, String packageName) {
if (mEraseEsim) {

View File

@@ -159,6 +159,7 @@ public class Settings extends SettingsActivity {
public static class WebViewAppPickerActivity extends SettingsActivity { /* empty */ }
public static class AdvancedConnectedDeviceActivity extends SettingsActivity { /* empty */ }
public static class BluetoothDeviceDetailActivity extends SettingsActivity { /* empty */ }
public static class WifiCallingDisclaimerActivity extends SettingsActivity { /* empty */ }
// Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {}

View File

@@ -17,6 +17,7 @@
package com.android.settings.accounts;
import android.accounts.Account;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.content.ContentResolver;
import android.content.Context;
@@ -64,11 +65,13 @@ public class AvatarViewMixin implements LifecycleObserver {
private final Context mContext;
private final ImageView mAvatarView;
private final MutableLiveData<Bitmap> mAvatarImage;
private final ActivityManager mActivityManager;
private String mAccountName;
public AvatarViewMixin(SettingsHomepageActivity activity, ImageView avatarView) {
mContext = activity.getApplicationContext();
mActivityManager = mContext.getSystemService(ActivityManager.class);
mAvatarView = avatarView;
mAvatarView.setOnClickListener(v -> {
Intent intent;
@@ -114,7 +117,11 @@ public class AvatarViewMixin implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
if (!mContext.getResources().getBoolean(R.bool.config_show_avatar_in_homepage)) {
Log.d(TAG, "Feature disabled. Skipping");
Log.d(TAG, "Feature disabled by config. Skipping");
return;
}
if (mActivityManager.isLowRamDevice()) {
Log.d(TAG, "Feature disabled on low ram device. Skipping");
return;
}
if (hasAccount()) {

View File

@@ -58,9 +58,14 @@ public class FaceEnrollAccessibilityToggle extends LinearLayout {
a.recycle();
}
mSwitch = findViewById(R.id.toggle);
mSwitch.setChecked(false);
}
public boolean isChecked() {
return mSwitch.isChecked();
}
public void setChecked(boolean checked) {
mSwitch.setChecked(checked);
}
}

View File

@@ -53,6 +53,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
final LinearLayout accessibilityLayout = findViewById(R.id.accessibility_layout);
final Button accessibilityButton = findViewById(R.id.accessibility_button);
accessibilityButton.setOnClickListener(view -> {
mSwitchDiversity.setChecked(true);
accessibilityButton.setVisibility(View.INVISIBLE);
accessibilityLayout.setVisibility(View.VISIBLE);
});
@@ -177,7 +178,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
} else {
intent.setClass(this, FaceEnrollEnrolling.class);
}
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, mSwitchDiversity.isChecked());
intent.putExtra(EXTRA_KEY_REQUIRE_DIVERSITY, !mSwitchDiversity.isChecked());
WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent);
return intent;
}

View File

@@ -26,4 +26,7 @@ public class FeatureFlags {
public static final String NETWORK_INTERNET_V2 = "settings_network_and_internet_v2";
public static final String SLICE_INJECTION = "settings_slice_injection";
public static final String MAINLINE_MODULE = "settings_mainline_module";
public static final String WIFI_DETAILS_SAVED_SCREEN = "settings_wifi_details_saved_screen";
public static final String WIFI_DETAILS_DATAUSAGE_HEADER =
"settings_wifi_details_datausage_header";
}

View File

@@ -135,6 +135,7 @@ import com.android.settings.wifi.ConfigureWifiSettings;
import com.android.settings.wifi.WifiAPITest;
import com.android.settings.wifi.WifiInfo;
import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.calling.WifiCallingDisclaimerFragment;
import com.android.settings.wifi.calling.WifiCallingSettings;
import com.android.settings.wifi.p2p.WifiP2pSettings;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
@@ -260,6 +261,7 @@ public class SettingsGateway {
ConnectedDeviceDashboardFragment.class.getName(),
UsbDetailsFragment.class.getName(),
AppAndNotificationDashboardFragment.class.getName(),
WifiCallingDisclaimerFragment.class.getName(),
AccountDashboardFragment.class.getName(),
EnterprisePrivacySettings.class.getName(),
WebViewAppPicker.class.getName(),

View File

@@ -69,6 +69,8 @@ public class DataUsageSummaryPreference extends Preference {
private boolean mDefaultTextColorSet;
private int mDefaultTextColor;
private int mNumPlans;
/** The specified un-initialized value for cycle time */
private final long CYCLE_TIME_UNINITIAL_VALUE = 0;
/** The ending time of the billing cycle in milliseconds since epoch. */
private long mCycleEndTimeMs;
/** The time of the last update in standard milliseconds since the epoch */
@@ -94,6 +96,7 @@ public class DataUsageSummaryPreference extends Preference {
/** WiFi only mode */
private boolean mWifiMode;
private String mUsagePeriod;
private boolean mSingleWifi; // Shows only one specified WiFi network usage
public DataUsageSummaryPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -142,9 +145,10 @@ public class DataUsageSummaryPreference extends Preference {
notifyChanged();
}
void setWifiMode(boolean isWifiMode, String usagePeriod) {
void setWifiMode(boolean isWifiMode, String usagePeriod, boolean isSingleWifi) {
mWifiMode = isWifiMode;
mUsagePeriod = usagePeriod;
mSingleWifi = isSingleWifi;
notifyChanged();
}
@@ -171,7 +175,16 @@ public class DataUsageSummaryPreference extends Preference {
Button launchButton = (Button) holder.findViewById(R.id.launch_mdp_app_button);
TextView limitInfo = (TextView) holder.findViewById(R.id.data_limits);
if (mWifiMode) {
if (mWifiMode && mSingleWifi) {
updateCycleTimeText(holder);
usageTitle.setVisibility(View.GONE);
launchButton.setVisibility(View.GONE);
carrierInfo.setVisibility(View.GONE);
limitInfo.setVisibility(TextUtils.isEmpty(mLimitInfoText) ? View.GONE : View.VISIBLE);
limitInfo.setText(mLimitInfoText);
} else if (mWifiMode) {
usageTitle.setText(R.string.data_usage_wifi_title);
usageTitle.setVisibility(View.VISIBLE);
TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
@@ -265,6 +278,13 @@ public class DataUsageSummaryPreference extends Preference {
private void updateCycleTimeText(PreferenceViewHolder holder) {
TextView cycleTime = (TextView) holder.findViewById(R.id.cycle_left_time);
// Takes zero as a special case which value is never set.
if (mCycleEndTimeMs == CYCLE_TIME_UNINITIAL_VALUE) {
cycleTime.setVisibility(View.GONE);
return;
}
cycleTime.setVisibility(View.VISIBLE);
long millisLeft = mCycleEndTimeMs - System.currentTimeMillis();
if (millisLeft <= 0) {
cycleTime.setText(getContext().getString(R.string.billing_cycle_none_left));

View File

@@ -65,10 +65,10 @@ public class DataUsageSummaryPreferenceController extends BasePreferenceControll
private final EntityHeaderController mEntityHeaderController;
private final Lifecycle mLifecycle;
private final PreferenceFragmentCompat mFragment;
private final DataUsageController mDataUsageController;
private final DataUsageInfoController mDataInfoController;
protected final DataUsageController mDataUsageController;
protected final DataUsageInfoController mDataInfoController;
private final NetworkTemplate mDefaultTemplate;
private final NetworkPolicyEditor mPolicyEditor;
protected final NetworkPolicyEditor mPolicyEditor;
private final int mDataUsageTemplate;
private final boolean mHasMobileData;
private final SubscriptionManager mSubscriptionManager;
@@ -200,11 +200,13 @@ public class DataUsageSummaryPreferenceController extends BasePreferenceControll
if (DataUsageUtils.hasSim(mActivity)) {
info = mDataUsageController.getDataUsageInfo(mDefaultTemplate);
mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(mDefaultTemplate));
summaryPreference.setWifiMode(/* isWifiMode */ false, /* usagePeriod */ null);
summaryPreference.setWifiMode(/* isWifiMode */ false,
/* usagePeriod */ null, /* isSingleWifi */ false);
} else {
info = mDataUsageController.getDataUsageInfo(
NetworkTemplate.buildTemplateWifiWildcard());
summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */ info.period);
summaryPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */
info.period, /* isSingleWifi */ false);
summaryPreference.setLimitInfo(null);
summaryPreference.setUsageNumbers(info.usageLevel,
/* dataPlanSize */ -1L,

View File

@@ -0,0 +1,72 @@
/*
* 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.datausage;
import android.app.Activity;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionManager;
import android.text.format.Formatter;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.net.DataUsageController;
/**
* The controller displays a data usage chart for the specified Wi-Fi network.
*/
public class WifiDataUsageSummaryPreferenceController extends DataUsageSummaryPreferenceController {
final String mNetworkId;
public WifiDataUsageSummaryPreferenceController(Activity activity,
Lifecycle lifecycle, PreferenceFragmentCompat fragment, CharSequence networkId) {
super(activity, lifecycle, fragment, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (networkId == null) {
mNetworkId = null;
} else {
mNetworkId = String.valueOf(networkId);
}
}
@Override
public void updateState(Preference preference) {
if (preference == null) {
return;
}
final DataUsageSummaryPreference mPreference = (DataUsageSummaryPreference) preference;
// TODO(b/126299427): Currently gets data usage of whole Wi-Fi networks, but should get
// specified one.
final NetworkTemplate template = NetworkTemplate.buildTemplateWifi(mNetworkId);
final DataUsageController.DataUsageInfo info = mDataUsageController.getDataUsageInfo(
template);
mDataInfoController.updateDataLimit(info, mPolicyEditor.getPolicy(template));
mPreference.setWifiMode(/* isWifiMode */ true, /* usagePeriod */
info.period, /* isSingleWifi */ true);
mPreference.setChartEnabled(true);
// Treats Wi-Fi network as unlimited network, which has same usage level and limited level.
mPreference.setUsageNumbers(info.usageLevel, info.usageLevel, /* hasMobileData */ false);
// TODO(b/126142293): Passpoint Wi-Fi should have limit of data usage and time remaining
mPreference.setProgress(100);
mPreference.setLabels(Formatter.formatFileSize(mContext, /* sizeBytes */ 0),
DataUsageUtils.formatDataUsage(mContext, info.usageLevel));
}
}

View File

@@ -17,6 +17,7 @@
package com.android.settings.homepage;
import android.animation.LayoutTransition;
import android.app.ActivityManager;
import android.app.settings.SettingsEnums;
import android.os.Bundle;
import android.view.View;
@@ -53,7 +54,10 @@ public class SettingsHomepageActivity extends SettingsBaseActivity {
final AvatarViewMixin avatarViewMixin = new AvatarViewMixin(this, avatarView);
getLifecycle().addObserver(avatarViewMixin);
showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
// Only allow contextual feature on high ram devices.
showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
}
showFragment(new TopLevelSettings(), R.id.main_content);
((FrameLayout) findViewById(R.id.main_content))
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);

View File

@@ -67,7 +67,8 @@ public class ContextualCardLoader extends AsyncLoaderCompat<List<ContextualCard>
@VisibleForTesting
Uri mNotifyUri;
private Context mContext;
private final Context mContext;
ContextualCardLoader(Context context) {
super(context);

View File

@@ -109,13 +109,13 @@ public class ContextualCardManager implements ContextualCardLoader.CardContentLo
}
}
void loadContextualCards(ContextualCardsFragment fragment) {
void loadContextualCards(LoaderManager loaderManager) {
mStartTime = System.currentTimeMillis();
final CardContentLoaderCallbacks cardContentLoaderCallbacks =
new CardContentLoaderCallbacks(mContext);
cardContentLoaderCallbacks.setListener(this);
// Use the cached data when navigating back to the first page and upon screen rotation.
LoaderManager.getInstance(fragment).initLoader(CARD_CONTENT_LOADER_ID, null /* bundle */,
loaderManager.initLoader(CARD_CONTENT_LOADER_ID, null /* bundle */,
cardContentLoaderCallbacks);
}

View File

@@ -19,16 +19,19 @@ package com.android.settings.homepage.contextualcards;
import static com.android.settings.homepage.contextualcards.ContextualCardsAdapter.SPAN_COUNT;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.loader.app.LoaderManager;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.overlay.FeatureFactory;
public class ContextualCardsFragment extends InstrumentedFragment {
@@ -42,14 +45,19 @@ public class ContextualCardsFragment extends InstrumentedFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContextualCardManager = new ContextualCardManager(getContext(), getSettingsLifecycle(),
final Context context = getContext();
if (savedInstanceState == null) {
FeatureFactory.getFactory(context).getSlicesFeatureProvider().newUiSession();
}
mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(),
savedInstanceState);
}
@Override
public void onStart() {
super.onStart();
mContextualCardManager.loadContextualCards(this);
mContextualCardManager.loadContextualCards(LoaderManager.getInstance(this));
}
@Override

View File

@@ -74,6 +74,7 @@ public class RecentLocationAccessPreferenceController extends AbstractPreference
mController = AppEntitiesHeaderController.newInstance(mContext, view)
.setHeaderTitleRes(R.string.location_category_recent_location_access)
.setHeaderDetailsRes(R.string.location_recent_location_access_view_details)
.setHeaderEmptyRes(R.string.location_no_recent_accesses)
.setHeaderDetailsClickListener((View v) -> {
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE);
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
@@ -100,14 +101,20 @@ public class RecentLocationAccessPreferenceController extends AbstractPreference
.setIcon(access.icon)
.setTitle(access.label)
.setSummary(access.contentDescription)
.setOnClickListener((v) -> {
final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION);
intent.putExtra(Intent.EXTRA_PERMISSION_NAME,
Manifest.permission_group.LOCATION);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName);
intent.putExtra(Intent.EXTRA_USER, access.userHandle);
mContext.startActivity(intent);
})
.build();
mController.setAppEntity(i, appEntityInfo);
}
for (; i < MAXIMUM_APP_COUNT; i++) {
mController.removeAppEntity(i);
}
} else {
// If there's no item to display, add a "No recent apps" item.
}
mController.apply();
}

View File

@@ -24,14 +24,10 @@ import android.content.Intent;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.EuiccManager;
import android.util.ArrayMap;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.network.telephony.MobileNetworkActivity;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -39,6 +35,12 @@ import com.android.settingslib.core.AbstractPreferenceController;
import java.util.List;
import java.util.Map;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
/**
* This populates the entries on a page which lists all available mobile subscriptions. Each entry
* has the name of the subscription with some subtext giving additional detail, and clicking on the
@@ -48,6 +50,9 @@ public class MobileNetworkListController extends AbstractPreferenceController im
LifecycleObserver, SubscriptionsChangeListener.SubscriptionsChangeListenerClient {
private static final String TAG = "MobileNetworkListCtlr";
@VisibleForTesting
static final String KEY_ADD_MORE = "add_more";
private SubscriptionManager mSubscriptionManager;
private SubscriptionsChangeListener mChangeListener;
private PreferenceScreen mPreferenceScreen;
@@ -76,6 +81,8 @@ public class MobileNetworkListController extends AbstractPreferenceController im
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreferenceScreen = screen;
final EuiccManager euiccManager = mContext.getSystemService(EuiccManager.class);
mPreferenceScreen.findPreference(KEY_ADD_MORE).setVisible(euiccManager.isEnabled());
update();
}
@@ -93,7 +100,7 @@ public class MobileNetworkListController extends AbstractPreferenceController im
final List<SubscriptionInfo> subscriptions = SubscriptionUtil.getAvailableSubscriptions(
mSubscriptionManager);
for (SubscriptionInfo info : subscriptions) {
int subId = info.getSubscriptionId();
final int subId = info.getSubscriptionId();
Preference pref = existingPreferences.remove(subId);
if (pref == null) {
pref = new Preference(mPreferenceScreen.getContext());

View File

@@ -16,8 +16,6 @@
package com.android.settings.network;
import static android.telephony.TelephonyManager.MultiSimVariants.UNKNOWN;
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
@@ -25,7 +23,6 @@ import android.content.Context;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import com.android.settings.R;
@@ -52,7 +49,6 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
private SubscriptionManager mSubscriptionManager;
private SubscriptionsChangeListener mChangeListener;
private TelephonyManager mTelephonyMgr;
private EuiccManager mEuiccManager;
private AddPreference mPreference;
@@ -74,7 +70,6 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
public MobileNetworkSummaryController(Context context, Lifecycle lifecycle) {
super(context);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mTelephonyMgr = mContext.getSystemService(TelephonyManager.class);
mEuiccManager = mContext.getSystemService(EuiccManager.class);
if (lifecycle != null) {
mChangeListener = new SubscriptionsChangeListener(context, this);
@@ -124,48 +119,43 @@ public class MobileNetworkSummaryController extends AbstractPreferenceController
mContext.startActivity(intent);
}
private boolean shouldShowAddButton() {
// The add button should only show up if the device is in multi-sim mode and the eSIM
// manager is enabled.
return mTelephonyMgr.getMultiSimConfiguration() != UNKNOWN && mEuiccManager.isEnabled();
}
private void update() {
if (mPreference == null) {
return;
}
final boolean showAddButton = shouldShowAddButton();
refreshSummary(mPreference);
if (!showAddButton) {
mPreference.setOnAddClickListener(null);
} else {
mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn());
mPreference.setOnAddClickListener(p -> {
startAddSimFlow();
});
}
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(
mSubscriptionManager);
mPreference.setOnPreferenceClickListener(null);
mPreference.setOnAddClickListener(null);
mPreference.setFragment(null);
mPreference.setEnabled(!mChangeListener.isAirplaneModeOn());
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(
mSubscriptionManager);
if (subs.isEmpty()) {
if (showAddButton) {
mPreference.setEnabled(false);
} else if (mEuiccManager.isEnabled()) {
if (mEuiccManager.isEnabled()) {
mPreference.setOnPreferenceClickListener((Preference pref) -> {
startAddSimFlow();
return true;
});
}
} else if (subs.size() == 1) {
mPreference.setOnPreferenceClickListener((Preference pref) -> {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
mContext.startActivity(intent);
return true;
});
} else {
mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());
// We have one or more existing subscriptions, so we want the plus button if eSIM is
// supported.
if (mEuiccManager.isEnabled()) {
mPreference.setAddWidgetEnabled(!mChangeListener.isAirplaneModeOn());
mPreference.setOnAddClickListener(p -> startAddSimFlow());
}
if (subs.size() == 1) {
mPreference.setOnPreferenceClickListener((Preference pref) -> {
final Intent intent = new Intent(mContext, MobileNetworkActivity.class);
mContext.startActivity(intent);
return true;
});
} else {
mPreference.setFragment(MobileNetworkListFragment.class.getCanonicalName());
}
}
}

View File

@@ -318,7 +318,8 @@ public class NetworkSelectSettings extends DashboardFragment {
// Try to get the network registration states
ServiceState ss = mTelephonyManager.getServiceState();
List<NetworkRegistrationState> networkList =
ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
ss.getNetworkRegistrationStatesForTransportType(
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
if (networkList == null || networkList.size() == 0) {
// Remove the connected network operators category
mConnectedPreferenceCategory.setVisible(false);

View File

@@ -152,6 +152,10 @@ public class AppNotificationSettings extends NotificationSettingsBase {
context, mImportanceListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new MinImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new HighImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,
mImportanceListener, mBackend));
mControllers.add(new LightsPreferenceController(context, mBackend));

View File

@@ -30,7 +30,7 @@ public class BubblePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String TAG = "BubblePrefContr";
private static final String KEY = "bubble";
private static final String KEY = "bubble_pref";
private static final int SYSTEM_WIDE_ON = 1;
private static final int SYSTEM_WIDE_OFF = 0;

View File

@@ -94,9 +94,12 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
mControllers = new ArrayList<>();
mControllers.add(new HeaderPreferenceController(context, this));
mControllers.add(new BlockPreferenceController(context, mImportanceListener, mBackend));
mControllers.add(new ImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new MinImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new HighImportancePreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new AllowSoundPreferenceController(
context, mImportanceListener, mBackend));
mControllers.add(new SoundPreferenceController(context, this,

View File

@@ -0,0 +1,84 @@
/*
* 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.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import android.app.NotificationChannel;
import android.content.Context;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.RestrictedSwitchPreference;
import androidx.preference.Preference;
public class HighImportancePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_IMPORTANCE = "high_importance";
private NotificationSettingsBase.ImportanceListener mImportanceListener;
public HighImportancePreferenceController(Context context,
NotificationSettingsBase.ImportanceListener importanceListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
}
@Override
public String getPreferenceKey() {
return KEY_IMPORTANCE;
}
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
return false;
}
if (mChannel == null) {
return false;
}
if (isDefaultChannel()) {
return false;
}
return mChannel.getImportance() >= IMPORTANCE_DEFAULT;
}
@Override
public void updateState(Preference preference) {
if (mAppRow!= null && mChannel != null) {
preference.setEnabled(mAdmin == null && isChannelConfigurable());
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
pref.setChecked(mChannel.getImportance() >= IMPORTANCE_HIGH);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mChannel != null) {
final boolean checked = (boolean) newValue;
mChannel.setImportance(checked ? IMPORTANCE_HIGH : IMPORTANCE_DEFAULT);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
}
return true;
}
}

View File

@@ -0,0 +1,172 @@
/*
* 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.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.util.ArrayMap;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import com.android.settingslib.R;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
public class ImportancePreference extends Preference {
boolean mIsBlockable = true;
boolean mIsConfigurable = true;
int mImportance;
ImageButton blockButton;
ImageButton silenceButton;
ImageButton alertButton;
ArrayMap<ImageButton, Integer> mImageButtons = new ArrayMap<>();
Context mContext;
public ImportancePreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
public ImportancePreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public ImportancePreference(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ImportancePreference(Context context) {
super(context);
init(context);
}
private void init(Context context) {
mContext = context;
setLayoutResource(R.layout.notif_importance_preference);
}
public void setImportance(int importance) {
mImportance = importance;
}
public void setBlockable(boolean blockable) {
mIsBlockable = blockable;
}
public void setConfigurable(boolean configurable) {
mIsConfigurable = configurable;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View blockView = holder.itemView.findViewById(R.id.block);
View alertView = holder.itemView.findViewById(R.id.alert);
View silenceView = holder.itemView.findViewById(R.id.silence);
if (!mIsBlockable) {
blockView.setVisibility(View.GONE);
if (mImportance == IMPORTANCE_NONE) {
mImportance = IMPORTANCE_LOW;
callChangeListener(IMPORTANCE_LOW);
}
}
blockButton = blockView.findViewById(R.id.block_icon);
silenceButton = silenceView.findViewById(R.id.silence_icon);
alertButton = alertView.findViewById(R.id.alert_icon);
mImageButtons.put(blockButton, mContext.getColor(R.color.notification_block_color));
mImageButtons.put(silenceButton, mContext.getColor(R.color.notification_silence_color));
mImageButtons.put(alertButton, mContext.getColor(R.color.notification_alert_color));
switch (mImportance) {
case IMPORTANCE_NONE:
colorizeImageButton(blockButton.getId());
if (!mIsConfigurable) {
alertView.setVisibility(View.GONE);
silenceView.setVisibility(View.GONE);
}
break;
case IMPORTANCE_MIN:
case IMPORTANCE_LOW:
colorizeImageButton(silenceButton.getId());
if (!mIsConfigurable) {
alertView.setVisibility(View.GONE);
blockView.setVisibility(View.GONE);
}
break;
case IMPORTANCE_HIGH:
default:
colorizeImageButton(alertButton.getId());
if (!mIsConfigurable) {
blockView.setVisibility(View.GONE);
silenceView.setVisibility(View.GONE);
}
break;
}
blockButton.setOnClickListener(v -> {
callChangeListener(IMPORTANCE_NONE);
colorizeImageButton(blockButton.getId());
});
silenceButton.setOnClickListener(v -> {
callChangeListener(IMPORTANCE_LOW);
colorizeImageButton(silenceButton.getId());
});
alertButton.setOnClickListener(v -> {
callChangeListener(IMPORTANCE_DEFAULT);
colorizeImageButton(alertButton.getId());
});
}
private void colorizeImageButton(int buttonId) {
if (mImageButtons != null) {
for (int i = 0; i < mImageButtons.size(); i++) {
final ImageButton imageButton = mImageButtons.keyAt(i);
final int color = mImageButtons.valueAt(i);
if (imageButton != null) {
LayerDrawable drawable = (LayerDrawable) imageButton.getDrawable();
Drawable foreground = drawable.findDrawableByLayerId(R.id.fore);
GradientDrawable background =
(GradientDrawable) drawable.findDrawableByLayerId(R.id.back);
if (buttonId == imageButton.getId()) {
foreground.setTint(Color.WHITE);
background.setColor(color);
} else {
foreground.setTint(color);
background.setColor(Color.TRANSPARENT);
}
}
}
}
}
}

View File

@@ -18,21 +18,15 @@ package com.android.settings.notification;
import static android.app.NotificationChannel.USER_LOCKED_SOUND;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.media.RingtoneManager;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.RestrictedListPreference;
import com.android.settings.core.PreferenceControllerMixin;
import androidx.preference.Preference;
public class ImportancePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
@@ -53,44 +47,33 @@ public class ImportancePreferenceController extends NotificationPreferenceContro
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
if (mAppRow == null) {
return false;
}
if (mChannel == null) {
return false;
}
return !isDefaultChannel();
if (isDefaultChannel()) {
return false;
}
return true;
}
@Override
public void updateState(Preference preference) {
if (mAppRow!= null && mChannel != null) {
preference.setEnabled(mAdmin == null && isChannelConfigurable());
preference.setSummary(getImportanceSummary(mChannel));
int importances = IMPORTANCE_HIGH - IMPORTANCE_MIN + 1;
CharSequence[] entries = new CharSequence[importances];
CharSequence[] values = new CharSequence[importances];
int index = 0;
for (int i = IMPORTANCE_HIGH; i >= IMPORTANCE_MIN; i--) {
NotificationChannel channel = new NotificationChannel("", "", i);
entries[index] = getImportanceSummary(channel);
values[index] = String.valueOf(i);
index++;
}
RestrictedListPreference pref = (RestrictedListPreference) preference;
pref.setEntries(entries);
pref.setEntryValues(values);
pref.setValue(String.valueOf(mChannel.getImportance()));
ImportancePreference pref = (ImportancePreference) preference;
pref.setBlockable(isChannelBlockable());
pref.setConfigurable(isChannelConfigurable());
pref.setImportance(mChannel.getImportance());
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mChannel != null) {
final int importance = Integer.parseInt((String) newValue);
final int importance = (Integer) newValue;
// If you are moving from an importance level without sound to one with sound,
// but the sound you had selected was "Silence",
@@ -111,39 +94,4 @@ public class ImportancePreferenceController extends NotificationPreferenceContro
}
return true;
}
protected String getImportanceSummary(NotificationChannel channel) {
String summary = "";
int importance = channel.getImportance();
switch (importance) {
case IMPORTANCE_UNSPECIFIED:
summary = mContext.getString(R.string.notification_importance_unspecified);
break;
case NotificationManager.IMPORTANCE_MIN:
summary = mContext.getString(R.string.notification_importance_min);
break;
case NotificationManager.IMPORTANCE_LOW:
summary = mContext.getString(R.string.notification_importance_low);
break;
case NotificationManager.IMPORTANCE_DEFAULT:
if (SoundPreferenceController.hasValidSound(channel)) {
summary = mContext.getString(R.string.notification_importance_default);
} else {
summary = mContext.getString(R.string.notification_importance_low);
}
break;
case NotificationManager.IMPORTANCE_HIGH:
case NotificationManager.IMPORTANCE_MAX:
if (SoundPreferenceController.hasValidSound(channel)) {
summary = mContext.getString(R.string.notification_importance_high);
} else {
summary = mContext.getString(R.string.notification_importance_high_silent);
}
break;
default:
return "";
}
return summary;
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import android.app.NotificationChannel;
import android.content.Context;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.RestrictedSwitchPreference;
import androidx.preference.Preference;
public class MinImportancePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String KEY_IMPORTANCE = "min_importance";
private NotificationSettingsBase.ImportanceListener mImportanceListener;
public MinImportancePreferenceController(Context context,
NotificationSettingsBase.ImportanceListener importanceListener,
NotificationBackend backend) {
super(context, backend);
mImportanceListener = importanceListener;
}
@Override
public String getPreferenceKey() {
return KEY_IMPORTANCE;
}
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
return false;
}
if (mChannel == null) {
return false;
}
if (isDefaultChannel()) {
return false;
}
return mChannel.getImportance() <= IMPORTANCE_LOW;
}
@Override
public void updateState(Preference preference) {
if (mAppRow!= null && mChannel != null) {
preference.setEnabled(mAdmin == null && isChannelConfigurable());
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
pref.setChecked(mChannel.getImportance() == IMPORTANCE_MIN);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mChannel != null) {
final boolean checked = (boolean) newValue;
mChannel.setImportance(checked ? IMPORTANCE_MIN : IMPORTANCE_LOW);
mChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
saveChannel();
mImportanceListener.onImportanceChanged();
}
return true;
}
}

View File

@@ -111,6 +111,9 @@ public abstract class NotificationPreferenceController extends AbstractPreferenc
}
protected boolean isChannelConfigurable() {
if (mAppRow != null && mAppRow.lockedImportance) {
return false;
}
if (mChannel != null && mAppRow != null) {
return !Objects.equals(mChannel.getId(), mAppRow.lockedChannelId);
}

View File

@@ -35,6 +35,7 @@ import com.android.settingslib.volume.MediaSessions;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
public class RemoteVolumePreferenceController extends
VolumeSeekBarPreferenceController {
@@ -58,14 +59,14 @@ public class RemoteVolumePreferenceController extends
if (mActiveToken == null) {
updateToken(token);
}
if (mActiveToken == token) {
if (Objects.equals(mActiveToken, token)) {
updatePreference(mPreference, mActiveToken, pi);
}
}
@Override
public void onRemoteRemoved(MediaSession.Token t) {
if (mActiveToken == t) {
if (Objects.equals(mActiveToken, t)) {
updateToken(null);
if (mPreference != null) {
mPreference.setVisible(false);
@@ -75,7 +76,7 @@ public class RemoteVolumePreferenceController extends
@Override
public void onRemoteVolumeChanged(MediaSession.Token token, int flags) {
if (mActiveToken == token) {
if (Objects.equals(mActiveToken, token)) {
final MediaController.PlaybackInfo pi = mMediaController.getPlaybackInfo();
if (pi != null) {
setSliderPosition(pi.getCurrentVolume());
@@ -116,13 +117,13 @@ public class RemoteVolumePreferenceController extends
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
super.onResume();
//TODO(b/126199571): register callback once b/126890783 is fixed
mMediaSessions.init();
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
super.onPause();
//TODO(b/126199571): unregister callback once b/126890783 is fixed
mMediaSessions.destroy();
}
@Override
@@ -189,8 +190,7 @@ public class RemoteVolumePreferenceController extends
@Override
public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() {
//TODO(b/126199571): return RemoteVolumeSliceWorker once b/126890783 is fixed
return null;
return RemoteVolumeSliceWorker.class;
}
private void updatePreference(VolumeSeekBarPreference seekBarPreference,

View File

@@ -171,6 +171,9 @@ public class ZenRulePreference extends TwoTargetPreference {
getSettingsActivity(rule, si);
mIntent = AbstractZenModeAutomaticRulePreferenceController.getRuleIntent(action,
settingsActivity, mId);
if (mIntent.resolveActivity(mPm) == null) {
mIntent = null;
}
setKey(mId);
}

View File

@@ -94,6 +94,7 @@ public class PanelSlicesAdapter
super(view);
sliceView = view.findViewById(R.id.slice_view);
sliceView.setMode(SliceView.MODE_LARGE);
sliceView.showTitleItems(true);
mPanelContent = panelContent;
}

View File

@@ -1,71 +0,0 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.slices;
import android.content.Context;
import android.net.Uri;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Manages custom {@link androidx.slice.Slice Slices}, which are all Slices not backed by
* preferences.
*/
public class CustomSliceManager {
private final Context mContext;
private final Map<Uri, CustomSliceable> mSliceableCache;
public CustomSliceManager(Context context) {
mContext = context.getApplicationContext();
mSliceableCache = new WeakHashMap<>();
}
/**
* Return a {@link CustomSliceable} associated to the Uri.
* <p>
* Do not change this method signature to accommodate for a special-case slicable - a context is
* the only thing that should be needed to create the object.
*/
public CustomSliceable getSliceableFromUri(Uri uri) {
final Uri newUri = CustomSliceRegistry.removeParameterFromUri(uri);
if (mSliceableCache.containsKey(newUri)) {
return mSliceableCache.get(newUri);
}
final Class clazz = CustomSliceRegistry.getSliceClassByUri(newUri);
if (clazz == null) {
throw new IllegalArgumentException("No Slice found for uri: " + uri);
}
final CustomSliceable sliceable = CustomSliceable.createInstance(mContext, clazz);
mSliceableCache.put(newUri, sliceable);
return sliceable;
}
/**
* Return a {@link CustomSliceable} associated to the Action.
* <p>
* Do not change this method signature to accommodate for a special-case sliceable - a context
* is the only thing that should be needed to create the object.
*/
public CustomSliceable getSliceableFromIntentAction(String action) {
return getSliceableFromUri(Uri.parse(action));
}
}

View File

@@ -322,7 +322,7 @@ public class CustomSliceRegistry {
}
public static Class<? extends CustomSliceable> getSliceClassByUri(Uri uri) {
return sUriToSlice.get(uri);
return sUriToSlice.get(removeParameterFromUri(uri));
}
public static Uri removeParameterFromUri(Uri uri) {
@@ -331,7 +331,7 @@ public class CustomSliceRegistry {
/**
* Returns {@code true} if {@param uri} is a valid Slice Uri handled by
* {@link CustomSliceManager}.
* {@link CustomSliceRegistry}.
*/
public static boolean isValidUri(Uri uri) {
return sUriToSlice.containsKey(removeParameterFromUri(uri));
@@ -339,7 +339,7 @@ public class CustomSliceRegistry {
/**
* Returns {@code true} if {@param action} is a valid intent action handled by
* {@link CustomSliceManager}.
* {@link CustomSliceRegistry}.
*/
public static boolean isValidAction(String action) {
return isValidUri(Uri.parse(action));

View File

@@ -19,7 +19,6 @@ package com.android.settings.slices;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import androidx.slice.Slice;
@@ -106,16 +105,17 @@ public interface CustomSliceable extends Sliceable {
/**
* Build an instance of a {@link CustomSliceable} which has a {@link Context}-only constructor.
*/
static CustomSliceable createInstance(Context context, Class<CustomSliceable> sliceableClass) {
static CustomSliceable createInstance(Context context,
Class<? extends CustomSliceable> sliceable) {
try {
final Constructor<CustomSliceable> sliceable =
sliceableClass.getConstructor(Context.class);
final Object[] params = new Object[]{context};
return sliceable.newInstance(params);
final Constructor<? extends CustomSliceable> constructor =
sliceable.getConstructor(Context.class);
final Object[] params = new Object[]{context.getApplicationContext()};
return constructor.newInstance(params);
} catch (NoSuchMethodException | InstantiationException |
IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(
"Invalid sliceable class: " + sliceableClass, e);
"Invalid sliceable class: " + sliceable, e);
}
}
}

View File

@@ -117,9 +117,6 @@ public class SettingsSliceProvider extends SliceProvider {
private static final KeyValueListParser KEY_VALUE_LIST_PARSER = new KeyValueListParser(',');
@VisibleForTesting
CustomSliceManager mCustomSliceManager;
@VisibleForTesting
SlicesDatabaseAccessor mSlicesDatabaseAccessor;
@@ -140,15 +137,15 @@ public class SettingsSliceProvider extends SliceProvider {
mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(getContext());
mSliceDataCache = new ConcurrentHashMap<>();
mSliceWeakDataCache = new WeakHashMap<>();
mCustomSliceManager = FeatureFactory.getFactory(
getContext()).getSlicesFeatureProvider().getCustomSliceManager(getContext());
return true;
}
@Override
public void onSlicePinned(Uri sliceUri) {
if (CustomSliceRegistry.isValidUri(sliceUri)) {
final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(sliceUri);
final Context context = getContext();
final CustomSliceable sliceable = FeatureFactory.getFactory(context)
.getSlicesFeatureProvider().getSliceableFromUri(context, sliceUri);
final IntentFilter filter = sliceable.getIntentFilter();
if (filter != null) {
registerIntentToUri(filter, sliceUri);
@@ -195,9 +192,10 @@ public class SettingsSliceProvider extends SliceProvider {
// Before adding a slice to {@link CustomSliceManager}, please get approval
// from the Settings team.
if (CustomSliceRegistry.isValidUri(sliceUri)) {
final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
sliceUri);
return sliceable.getSlice();
final Context context = getContext();
return FeatureFactory.getFactory(context)
.getSlicesFeatureProvider().getSliceableFromUri(context, sliceUri)
.getSlice();
}
if (CustomSliceRegistry.WIFI_CALLING_URI.equals(sliceUri)) {

View File

@@ -61,11 +61,10 @@ public class SliceBroadcastReceiver extends BroadcastReceiver {
final boolean isPlatformSlice = intent.getBooleanExtra(EXTRA_SLICE_PLATFORM_DEFINED,
false /* default */);
final CustomSliceManager mCustomSliceManager = FeatureFactory.getFactory(
context).getSlicesFeatureProvider().getCustomSliceManager(context);
if (CustomSliceRegistry.isValidAction(action)) {
final CustomSliceable sliceable =
mCustomSliceManager.getSliceableFromIntentAction(action);
CustomSliceable.createInstance(context,
CustomSliceRegistry.getSliceClassByUri(Uri.parse(action)));
sliceable.onNotifyChange(intent);
return;
}

View File

@@ -23,7 +23,6 @@ import android.util.Log;
import com.android.settings.bluetooth.BluetoothSliceBuilder;
import com.android.settings.notification.ZenModeSliceBuilder;
import com.android.settings.overlay.FeatureFactory;
public class SliceDeepLinkSpringBoard extends Activity {
@@ -45,11 +44,10 @@ public class SliceDeepLinkSpringBoard extends Activity {
Intent launchIntent;
// TODO (b/80263568) Avoid duplicating this list of Slice Uris.
final CustomSliceManager customSliceManager = FeatureFactory.getFactory(this)
.getSlicesFeatureProvider().getCustomSliceManager(this);
if (CustomSliceRegistry.isValidUri(sliceUri)) {
final CustomSliceable sliceable =
customSliceManager.getSliceableFromUri(sliceUri);
CustomSliceable.createInstance(getApplicationContext(),
CustomSliceRegistry.getSliceClassByUri(sliceUri));
launchIntent = sliceable.getIntent();
} else if (CustomSliceRegistry.ZEN_MODE_SLICE_URI.equals(sliceUri)) {
launchIntent = ZenModeSliceBuilder.getIntent(this /* context */);

View File

@@ -1,6 +1,7 @@
package com.android.settings.slices;
import android.content.Context;
import android.net.Uri;
import com.android.settings.network.telephony.Enhanced4gLteSliceHelper;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
@@ -12,10 +13,22 @@ public interface SlicesFeatureProvider {
boolean DEBUG = false;
SlicesIndexer getSliceIndexer(Context context);
SliceDataConverter getSliceDataConverter(Context context);
/**
* Starts a new UI session for the purpose of using Slices.
*
* A UI session is defined as an duration of time when user stays in a UI screen. Screen
* rotation does not break the continuation of session, going to a sub-page and coming out does
* not break the continuation either. Leaving the page and coming back breaks it.
*/
void newUiSession();
/**
* Returns the token created in {@link #newUiSession}.
*/
long getUiSessionToken();
/**
* Asynchronous call to index the data used to build Slices.
* If the data is already indexed, the data will not change.
@@ -28,7 +41,14 @@ public interface SlicesFeatureProvider {
*/
void indexSliceData(Context context);
CustomSliceManager getCustomSliceManager(Context context);
/**
* Return a {@link CustomSliceable} associated to the Uri.
* <p>
* Do not change this method signature to accommodate for a special-case sliceable - a context
* is the only thing that should be needed to create the object.
*/
CustomSliceable getSliceableFromUri(Context context, Uri uri);
/**
* Gets new WifiCallingSliceHelper object

View File

@@ -17,27 +17,23 @@
package com.android.settings.slices;
import android.content.Context;
import android.net.Uri;
import android.os.SystemClock;
import com.android.settings.network.telephony.Enhanced4gLteSliceHelper;
import com.android.settings.wifi.calling.WifiCallingSliceHelper;
import com.android.settingslib.utils.ThreadUtils;
import java.util.Random;
/**
* Manages Slices in Settings.
*/
public class SlicesFeatureProviderImpl implements SlicesFeatureProvider {
private long mUiSessionToken;
private SlicesIndexer mSlicesIndexer;
private SliceDataConverter mSliceDataConverter;
private CustomSliceManager mCustomSliceManager;
@Override
public SlicesIndexer getSliceIndexer(Context context) {
if (mSlicesIndexer == null) {
mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
}
return mSlicesIndexer;
}
@Override
public SliceDataConverter getSliceDataConverter(Context context) {
@@ -48,11 +44,13 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider {
}
@Override
public CustomSliceManager getCustomSliceManager(Context context) {
if (mCustomSliceManager == null) {
mCustomSliceManager = new CustomSliceManager(context.getApplicationContext());
}
return mCustomSliceManager;
public void newUiSession() {
mUiSessionToken = SystemClock.elapsedRealtime();
}
@Override
public long getUiSessionToken() {
return mUiSessionToken;
}
@Override
@@ -76,4 +74,23 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider {
public Enhanced4gLteSliceHelper getNewEnhanced4gLteSliceHelper(Context context) {
return new Enhanced4gLteSliceHelper(context);
}
@Override
public CustomSliceable getSliceableFromUri(Context context, Uri uri) {
final Uri newUri = CustomSliceRegistry.removeParameterFromUri(uri);
final Class clazz = CustomSliceRegistry.getSliceClassByUri(newUri);
if (clazz == null) {
throw new IllegalArgumentException("No Slice found for uri: " + uri);
}
final CustomSliceable sliceable = CustomSliceable.createInstance(context, clazz);
return sliceable;
}
private SlicesIndexer getSliceIndexer(Context context) {
if (mSlicesIndexer == null) {
mSlicesIndexer = new SlicesIndexer(context.getApplicationContext());
}
return mSlicesIndexer;
}
}

View File

@@ -249,7 +249,7 @@ public class WifiConfigController implements TextWatcher,
mHiddenSettingsSpinner = mView.findViewById(R.id.hidden_settings);
mPrivacySettingsSpinner = mView.findViewById(R.id.privacy_settings);
if (mContext.getResources().getBoolean(
com.android.internal.R.bool.config_wifi_p2p_mac_randomization_supported)) {
com.android.internal.R.bool.config_wifi_connected_mac_randomization_supported)) {
View privacySettingsLayout = mView.findViewById(R.id.privacy_settings_fields);
privacySettingsLayout.setVisibility(View.VISIBLE);
}

View File

@@ -67,6 +67,7 @@ import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
import com.android.settings.widget.SwitchBarController;
import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
import com.android.settings.wifi.dpp.WifiDppUtils;
import com.android.settings.wifi.savedaccesspoints.SavedAccessPointsWifiSettings;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.search.SearchIndexable;
@@ -968,8 +969,13 @@ public class WifiSettings extends RestrictedSettingsFragment
}
private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) {
final AccessPoint accessPoint = pref.getAccessPoint();
final Context context = getContext();
final CharSequence title = SavedAccessPointsWifiSettings.usingDetailsFragment(context) ?
accessPoint.getTitle() : context.getText(R.string.pref_title_network_details);
new SubSettingLauncher(getContext())
.setTitleRes(R.string.pref_title_network_details)
.setTitleText(title)
.setDestination(WifiNetworkDetailsFragment.class.getName())
.setArguments(pref.getExtras())
.setSourceMetricsCategory(getMetricsCategory())

View File

@@ -0,0 +1,137 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
/**
* Interface to control disclaimer item from {@link WifiCallingDisclaimerFragment}.
*/
@VisibleForTesting
public abstract class DisclaimerItem {
private static final String SHARED_PREFERENCES_NAME = "wfc_disclaimer_prefs";
protected final Context mContext;
protected final int mSubId;
private final CarrierConfigManager mCarrierConfigManager;
DisclaimerItem(Context context, int subId) {
mContext = context;
mSubId = subId;
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
}
/**
* Called by the {@link WifiCallingDisclaimerFragment} when a user has clicked the agree button.
*/
void onAgreed() {
setBooleanSharedPrefs(getPrefKey(), true);
}
/**
* Checks whether the disclaimer item need to be displayed or not.
*
* @return Returns {@code true} if disclaimer item need to be displayed,
* {@code false} if not displayed.
*/
boolean shouldShow() {
if (getBooleanSharedPrefs(getPrefKey(), false)) {
logd("shouldShow: false due to a user has already agreed.");
return false;
}
logd("shouldShow: true");
return true;
}
/**
* Gets the configuration values for a particular sub id.
*
* @return The {@link PersistableBundle} instance containing the config value for a
* particular phone id, or default values.
*/
protected PersistableBundle getCarrierConfig() {
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(mSubId);
if (config != null) {
return config;
}
// Return static default defined in CarrierConfigManager.
return CarrierConfigManager.getDefaultConfig();
}
protected void logd(String msg) {
Log.d(getName(), "[" + mSubId + "] " + msg);
}
/**
* Gets a title id for disclaimer item.
*
* @return Title id for disclaimer item.
*/
protected abstract int getTitleId();
/**
* Gets a message id for disclaimer item.
*
* @return Message id for disclaimer item.
*/
protected abstract int getMessageId();
/**
* Gets a name of disclaimer item.
*
* @return Name of disclaimer item.
*/
protected abstract String getName();
/**
* Gets a preference key to keep user's consent.
*
* @return Preference key to keep user's consent.
*/
protected abstract String getPrefKey();
/**
* Gets the boolean value from shared preferences.
*
* @param key The key for the preference item.
* @param defValue Value to return if this preference does not exist.
* @return The boolean value of corresponding key, or defValue.
*/
private boolean getBooleanSharedPrefs(String key, boolean defValue) {
SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
return prefs.getBoolean(key + mSubId, defValue);
}
/**
* Sets the boolean value to shared preferences.
*
* @param key The key for the preference item.
* @param value The value to be set for shared preferences.
*/
private void setBooleanSharedPrefs(String key, boolean value) {
SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
prefs.edit().putBoolean(key + mSubId, value).apply();
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import android.content.Context;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Factory class to create disclaimer items list.
*/
@VisibleForTesting
public final class DisclaimerItemFactory {
/**
* Creates disclaimer items list.
*
* @param context The application context
* @param subId The subscription id.
* @return The {@link DisclaimerItem} list instance, if there are no items, return empty list.
*/
public static List<DisclaimerItem> create(Context context, int subId) {
List<DisclaimerItem> itemList = getDisclaimerItemList(context, subId);
Iterator itr = itemList.iterator();
while (itr.hasNext()) {
DisclaimerItem item = (DisclaimerItem) itr.next();
if (!item.shouldShow()) {
itr.remove();
}
}
return itemList;
}
private static List<DisclaimerItem> getDisclaimerItemList(Context context, int subId) {
List<DisclaimerItem> itemList = new ArrayList<DisclaimerItem>();
return itemList;
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import java.util.List;
/**
* Adapter for disclaimer items list.
*/
public class DisclaimerItemListAdapter extends
RecyclerView.Adapter<DisclaimerItemListAdapter.DisclaimerItemViewHolder> {
private List<DisclaimerItem> mDisclaimerItemList;
public DisclaimerItemListAdapter(List<DisclaimerItem> list) {
mDisclaimerItemList = list;
}
@Override
public DisclaimerItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = (LayoutInflater) parent.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.wfc_simple_disclaimer_item, null, false);
return new DisclaimerItemViewHolder(view);
}
@Override
public void onBindViewHolder(DisclaimerItemViewHolder holder, int position) {
holder.titleView.setText(mDisclaimerItemList.get(position).getTitleId());
holder.descriptionView.setText(mDisclaimerItemList.get(position).getMessageId());
}
@Override
public int getItemCount() {
return mDisclaimerItemList.size();
}
public static class DisclaimerItemViewHolder extends RecyclerView.ViewHolder {
@VisibleForTesting
static final int ID_DISCLAIMER_ITEM_TITLE = R.id.disclaimer_title;
@VisibleForTesting
static final int ID_DISCLAIMER_ITEM_DESCRIPTION = R.id.disclaimer_desc;
public final TextView titleView;
public final TextView descriptionView;
public DisclaimerItemViewHolder(View itemView) {
super(itemView);
titleView = itemView.findViewById(ID_DISCLAIMER_ITEM_TITLE);
descriptionView = itemView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION);
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import android.app.Activity;
import android.os.Bundle;
import android.telephony.SubscriptionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import java.util.ArrayList;
import java.util.List;
/**
* Fragment for displaying disclaimers for WFC.
*/
public class WifiCallingDisclaimerFragment extends InstrumentedFragment
implements View.OnClickListener {
private static final String TAG = "WifiCallingDisclaimerFragment";
private static final String STATE_IS_SCROLL_TO_BOTTOM = "state_is_scroll_to_bottom";
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<DisclaimerItem>();
private Button mAgreeButton;
private Button mDisagreeButton;
private boolean mScrollToBottom;
@Override
public int getMetricsCategory() {
return MetricsEvent.WIFI_CALLING;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle args = getArguments();
final int subId = (args != null) ? args.getInt(WifiCallingSettingsForSub.EXTRA_SUB_ID)
: SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
mDisclaimerItemList = DisclaimerItemFactory.create(getActivity(), subId);
if (mDisclaimerItemList.isEmpty()) {
finish(Activity.RESULT_OK);
return;
}
if (savedInstanceState != null) {
mScrollToBottom = savedInstanceState.getBoolean(
STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.wfc_disclaimer_fragment, container, false);
mAgreeButton = view.findViewById(R.id.agree_button);
mAgreeButton.setOnClickListener(this);
mDisagreeButton = view.findViewById(R.id.disagree_button);
mDisagreeButton.setOnClickListener(this);
final RecyclerView recyclerView = (RecyclerView) view.findViewById(
R.id.disclaimer_item_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(new DisclaimerItemListAdapter(mDisclaimerItemList));
RecyclerView.ItemDecoration itemDecoration =
new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL);
recyclerView.addItemDecoration(itemDecoration);
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (!recyclerView.canScrollVertically(1 /* scrolling down */)) {
mScrollToBottom = true;
updateButtonState();
recyclerView.removeOnScrollListener(this);
}
}
});
return view;
}
@Override
public void onResume() {
super.onResume();
updateButtonState();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(STATE_IS_SCROLL_TO_BOTTOM, mScrollToBottom);
}
private void updateButtonState() {
mAgreeButton.setEnabled(mScrollToBottom);
}
@Override
public void onClick(View v) {
if (v == mAgreeButton) {
for (DisclaimerItem item : mDisclaimerItemList) {
item.onAgreed();
}
finish(Activity.RESULT_OK);
} else if (v == mDisagreeButton) {
finish(Activity.RESULT_CANCELED);
}
}
@VisibleForTesting
void finish(int result) {
Activity activity = getActivity();
activity.setResult(result, null);
activity.finish();
}
}

View File

@@ -53,6 +53,7 @@ import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.widget.SwitchBar;
/**
@@ -69,9 +70,13 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String PREFERENCE_EMERGENCY_ADDRESS = "emergency_address_key";
private static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
@VisibleForTesting
static final int REQUEST_CHECK_WFC_EMERGENCY_ADDRESS = 1;
@VisibleForTesting
static final int REQUEST_CHECK_WFC_DISCLAIMER = 2;
public static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
public static final String EXTRA_SUB_ID = "EXTRA_SUB_ID";
protected static final String FRAGMENT_BUNDLE_SUBID = "subId";
@@ -172,7 +177,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
mEmptyView = getView().findViewById(android.R.id.empty);
setEmptyView(mEmptyView);
final Resources res = SubscriptionManager.getResourcesForSubId(getContext(), mSubId);
final Resources res = getResourcesForSubId();
String emptyViewText = res.getString(R.string.wifi_calling_off_explanation,
res.getString(R.string.wifi_calling_off_explanation_2));
mEmptyView.setText(emptyViewText);
@@ -416,14 +421,17 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
return;
}
// Call address management activity before turning on WFC
Intent carrierAppIntent = getCarrierActivityIntent();
if (carrierAppIntent != null) {
carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
startActivityForResult(carrierAppIntent, REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
} else {
updateWfcMode(true);
}
// Launch disclaimer fragment before turning on WFC
final Context context = getActivity();
final Bundle args = new Bundle();
args.putInt(EXTRA_SUB_ID, mSubId);
new SubSettingLauncher(context)
.setDestination(WifiCallingDisclaimerFragment.class.getName())
.setArguments(args)
.setTitleRes(R.string.wifi_calling_settings_title)
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, REQUEST_CHECK_WFC_DISCLAIMER)
.launch();
}
/*
@@ -476,12 +484,30 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
final Context context = getActivity();
if (requestCode == REQUEST_CHECK_WFC_EMERGENCY_ADDRESS) {
Log.d(TAG, "WFC emergency address activity result = " + resultCode);
Log.d(TAG, "WFC activity request = " + requestCode + " result = " + resultCode);
if (resultCode == Activity.RESULT_OK) {
updateWfcMode(true);
}
switch (requestCode) {
case REQUEST_CHECK_WFC_EMERGENCY_ADDRESS:
if (resultCode == Activity.RESULT_OK) {
updateWfcMode(true);
}
break;
case REQUEST_CHECK_WFC_DISCLAIMER:
if (resultCode == Activity.RESULT_OK) {
// Call address management activity before turning on WFC
Intent carrierAppIntent = getCarrierActivityIntent();
if (carrierAppIntent != null) {
carrierAppIntent.putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUCH_APP_ACTIVATE);
startActivityForResult(carrierAppIntent,
REQUEST_CHECK_WFC_EMERGENCY_ADDRESS);
} else {
updateWfcMode(true);
}
}
break;
default:
Log.e(TAG, "Unexpected request: " + requestCode);
break;
}
}
@@ -568,4 +594,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
}
return resId;
}
@VisibleForTesting
Resources getResourcesForSubId() {
return SubscriptionManager.getResourcesForSubId(getContext(), mSubId, false);
}
}

View File

@@ -42,6 +42,7 @@ import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.widget.ImageView;
import android.widget.Toast;
@@ -51,12 +52,14 @@ import androidx.core.text.BidiFormatter;
import androidx.fragment.app.Fragment;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.datausage.WifiDataUsageSummaryPreferenceController;
import com.android.settings.development.featureflags.FeatureFlagPersistent;
import com.android.settings.widget.EntityHeaderController;
import com.android.settings.wifi.WifiDialog;
@@ -150,6 +153,9 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
private Preference mDnsPref;
private PreferenceCategory mIpv6Category;
private Preference mIpv6AddressPref;
private Lifecycle mLifecycle;
Preference mDataUsageSummaryPref;
WifiDataUsageSummaryPreferenceController mSummaryHeaderController;
private final IconInjector mIconInjector;
private final IntentFilter mFilter;
@@ -262,6 +268,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
mLifecycle = lifecycle;
lifecycle.addObserver(this);
}
@@ -313,6 +320,17 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
private void setupEntityHeader(PreferenceScreen screen) {
LayoutPreference headerPref = screen.findPreference(KEY_HEADER);
if (usingDataUsageHeader(mContext)) {
headerPref.setVisible(false);
mDataUsageSummaryPref = screen.findPreference("status_header");
mDataUsageSummaryPref.setVisible(true);
mSummaryHeaderController =
new WifiDataUsageSummaryPreferenceController(mFragment.getActivity(),
mLifecycle, (PreferenceFragmentCompat) mFragment, mAccessPoint.getSsid());
return;
}
mEntityHeaderController =
EntityHeaderController.newInstance(
mFragment.getActivity(), mFragment,
@@ -326,6 +344,15 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
mEntityHeaderController.setLabel(mAccessPoint.getTitle());
}
private void refreshEntityHeader() {
if (usingDataUsageHeader(mContext)) {
mSummaryHeaderController.updateState(mDataUsageSummaryPref);
} else {
mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
.done(mFragment.getActivity(), true /* rebind */);
}
}
@Override
public void onResume() {
// Ensure mNetwork is set before any callbacks above are delivered, since our
@@ -360,9 +387,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
// MAC Address Pref
mMacAddressPref.setSummary(mWifiConfig.getRandomizedMacAddress().toString());
// TODO(b/124700353): Change header to data usage chart
mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
.done(mFragment.getActivity(), true /* rebind */);
refreshEntityHeader();
updateIpLayerInfo();
@@ -429,8 +454,7 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
private void refreshNetworkState() {
mAccessPoint.update(mWifiConfig, mWifiInfo, mNetworkInfo);
mEntityHeaderController.setSummary(mAccessPoint.getSettingsSummary())
.done(mFragment.getActivity(), true /* rebind */);
refreshEntityHeader();
}
private void refreshRssiViews() {
@@ -443,7 +467,10 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
Drawable wifiIcon = mIconInjector.getIcon(mRssiSignalLevel);
wifiIcon.setTintList(Utils.getColorAccent(mContext));
mEntityHeaderController.setIcon(wifiIcon).done(mFragment.getActivity(), true /* rebind */);
if (mEntityHeaderController != null) {
mEntityHeaderController.setIcon(wifiIcon).done(mFragment.getActivity(),
true /* rebind */);
}
Drawable wifiIconDark = wifiIcon.getConstantState().newDrawable().mutate();
wifiIconDark.setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal));
@@ -670,4 +697,8 @@ public class WifiDetailPreferenceController extends AbstractPreferenceController
return mContext.getDrawable(Utils.getWifiIconResource(level)).mutate();
}
}
private boolean usingDataUsageHeader(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER);
}
}

View File

@@ -153,11 +153,11 @@ public class WifiNetworkDetailsFragment extends DashboardFragment {
controllers.add(mWifiDetailPreferenceController);
controllers.add(new WifiMeteredPreferenceController(context, mAccessPoint.getConfig()));
WifiPrivacyPreferenceController preferenceController = new WifiPrivacyPreferenceController(
WifiPrivacyPreferenceController privacyController = new WifiPrivacyPreferenceController(
context);
preferenceController.setWifiConfiguration(mAccessPoint.getConfig());
preferenceController.setIsEphemeral(mAccessPoint.isEphemeral());
controllers.add(preferenceController);
privacyController.setWifiConfiguration(mAccessPoint.getConfig());
privacyController.setIsEphemeral(mAccessPoint.isEphemeral());
controllers.add(privacyController);
return controllers;
}

View File

@@ -59,7 +59,7 @@ public class WifiPrivacyPreferenceController extends BasePreferenceController im
@Override
public int getAvailabilityStatus() {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_wifi_p2p_mac_randomization_supported) ?
com.android.internal.R.bool.config_wifi_connected_mac_randomization_supported) ?
AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}

View File

@@ -199,11 +199,7 @@ public class SavedAccessPointsWifiSettings extends DashboardFragment
* Checks if showing WifiNetworkDetailsFragment when clicking saved network item.
*/
public static boolean usingDetailsFragment(Context context) {
if (FeatureFlagUtils.isEnabled(context, FeatureFlags.MOBILE_NETWORK_V2)
&& FeatureFlagPersistent.isEnabled(context, FeatureFlags.NETWORK_INTERNET_V2)) {
return false; // TODO(b/124695272): mark true when UI is ready.
}
return false;
return FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_SAVED_SCREEN);
}
boolean isSubscriptionsFeatureEnabled() {

View File

@@ -25,6 +25,7 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.slice.Slice;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.CustomSliceable;
@@ -35,7 +36,9 @@ public class ContextualWifiSlice extends WifiSlice {
private static final String TAG = "ContextualWifiSlice";
@VisibleForTesting
boolean mPreviouslyDisplayed;
static long sActiveUiSession = -1000;
@VisibleForTesting
static boolean sPreviouslyDisplayed;
public ContextualWifiSlice(Context context) {
super(context);
@@ -48,13 +51,19 @@ public class ContextualWifiSlice extends WifiSlice {
@Override
public Slice getSlice() {
if (!mPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) {
final long currentUiSession = FeatureFactory.getFactory(mContext)
.getSlicesFeatureProvider().getUiSessionToken();
if (currentUiSession != sActiveUiSession) {
sActiveUiSession = currentUiSession;
sPreviouslyDisplayed = false;
}
if (!sPreviouslyDisplayed && !TextUtils.equals(getActiveSSID(), WifiSsid.NONE)) {
Log.d(TAG, "Wifi is connected, no point showing any suggestion.");
return null;
}
// Set mPreviouslyDisplayed to true - we will show *something* on the screen. So we should
// 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.
mPreviouslyDisplayed = true;
sPreviouslyDisplayed = true;
return super.getSlice();
}

View File

@@ -6,6 +6,7 @@ com.android.settings.applications.appinfo.InstantAppButtonsPreferenceController
com.android.settings.bluetooth.BluetoothDeviceNamePreferenceController
com.android.settings.bluetooth.BluetoothDeviceRenamePreferenceController
com.android.settings.datausage.DataUsageSummaryPreferenceController
com.android.settings.datausage.WifiDataUsageSummaryPreferenceController
com.android.settings.fuelgauge.RestrictAppPreferenceController
com.android.settings.fuelgauge.batterysaver.BatterySaverButtonPreferenceController
com.android.settings.fuelgauge.batterytip.BatteryTipPreferenceController

View File

@@ -27,6 +27,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.accounts.Account;
import android.app.ActivityManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
@@ -55,6 +56,7 @@ import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowActivityManager;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowPackageManager;
@@ -100,6 +102,19 @@ public class AvatarViewMixinTest {
verify(mixin, never()).hasAccount();
}
@Test
public void onStart_lowRamDevice_doNothing() {
final AvatarViewMixin mixin = spy(new AvatarViewMixin(mActivity, mImageView));
final ShadowActivityManager activityManager =
Shadow.extract(mContext.getSystemService(ActivityManager.class));
activityManager.setIsLowRamDevice(true);
mixin.onStart();
verify(mixin, never()).hasAccount();
}
@Test
@Config(qualifiers = "mcc999",
shadows = {

View File

@@ -164,7 +164,8 @@ public class DataUsageSummaryPreferenceControllerTest {
verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
CARRIER_NAME, 1 /* numPlans */, intent);
verify(mSummaryPreference).setChartEnabled(true);
verify(mSummaryPreference).setWifiMode(false, null);
verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
false /* isSingleWifi */);
}
@Test
@@ -188,7 +189,8 @@ public class DataUsageSummaryPreferenceControllerTest {
verify(mSummaryPreference).setUsageInfo(info.cycleEnd, now - UPDATE_BACKOFF_MS,
CARRIER_NAME, 0 /* numPlans */, intent);
verify(mSummaryPreference).setChartEnabled(true);
verify(mSummaryPreference).setWifiMode(false, null);
verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
false /* isSingleWifi */);
}
@Test
@@ -214,7 +216,8 @@ public class DataUsageSummaryPreferenceControllerTest {
0 /* numPlans */,
null /* launchIntent */);
verify(mSummaryPreference).setChartEnabled(true);
verify(mSummaryPreference).setWifiMode(false, null);
verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
false /* isSingleWifi */);
}
@Test
@@ -240,7 +243,8 @@ public class DataUsageSummaryPreferenceControllerTest {
0 /* numPlans */,
null /* launchIntent */);
verify(mSummaryPreference).setChartEnabled(false);
verify(mSummaryPreference).setWifiMode(false, null);
verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
false /* isSingleWifi */);
}
@Test
@@ -321,7 +325,8 @@ public class DataUsageSummaryPreferenceControllerTest {
verify(mSummaryPreference).setLimitInfo(captor.capture());
CharSequence value = captor.getValue();
assertThat(value.toString()).isEqualTo("1.00 MB data warning / 1.00 MB data limit");
verify(mSummaryPreference).setWifiMode(false, null);
verify(mSummaryPreference).setWifiMode(false /* isWifiMode */, null /* usagePeriod */,
false /* isSingleWifi */);
}
@Test
@@ -340,7 +345,8 @@ public class DataUsageSummaryPreferenceControllerTest {
when(mTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_ABSENT);
mController.updateState(mSummaryPreference);
verify(mSummaryPreference).setWifiMode(true, info.period);
verify(mSummaryPreference).setWifiMode(true /* isWifiMode */, info.period /* usagePeriod */,
false /* isSingleWifi */);
verify(mSummaryPreference).setLimitInfo(null);
verify(mSummaryPreference).setUsageNumbers(info.usageLevel, -1L, true);
verify(mSummaryPreference).setChartEnabled(false);

View File

@@ -488,7 +488,7 @@ public class DataUsageSummaryPreferenceTest {
new Intent());
mSummaryPreference.setUsageNumbers(1000000L, -1L, true);
final String cycleText = "The quick fox";
mSummaryPreference.setWifiMode(true, cycleText);
mSummaryPreference.setWifiMode(true /* isWifiMode */, cycleText, false /* isSingleWifi */);
doReturn(200L).when(mSummaryPreference).getHistoricalUsageLevel();
bindViewHolder();
@@ -524,7 +524,8 @@ public class DataUsageSummaryPreferenceTest {
@Test
public void testSetWifiMode_noUsageInfo_shouldDisableLaunchButton() {
mSummaryPreference = spy(mSummaryPreference);
mSummaryPreference.setWifiMode(true, "Test cycle text");
mSummaryPreference.setWifiMode(true /* isWifiMode */, "Test cycle text",
false /* isSingleWifi */);
doReturn(0L).when(mSummaryPreference).getHistoricalUsageLevel();
bindViewHolder();

View File

@@ -94,13 +94,13 @@ public class BatterySaverButtonPreferenceControllerTest {
public void setChecked_on_setPowerSaveMode() {
mController.setChecked(true);
verify(mPowerManager).setPowerSaveMode(true);
verify(mPowerManager).setPowerSaveModeEnabled(true);
}
@Test
public void setChecked_off_unsetPowerSaveMode() {
mController.setChecked(false);
verify(mPowerManager).setPowerSaveMode(false);
verify(mPowerManager).setPowerSaveModeEnabled(false);
}
}

View File

@@ -21,6 +21,7 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -31,6 +32,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.telephony.SubscriptionInfo;
import android.telephony.euicc.EuiccManager;
import org.junit.After;
import org.junit.Before;
@@ -50,6 +52,9 @@ import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkListControllerTest {
@Mock
EuiccManager mEuiccManager;
@Mock
private Lifecycle mLifecycle;
@@ -58,12 +63,17 @@ public class MobileNetworkListControllerTest {
private Context mContext;
private MobileNetworkListController mController;
private Preference mAddMorePreference;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(Robolectric.setupActivity(Activity.class));
when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
when(mPreferenceScreen.getContext()).thenReturn(mContext);
mAddMorePreference = new Preference(mContext);
when(mPreferenceScreen.findPreference(MobileNetworkListController.KEY_ADD_MORE)).thenReturn(
mAddMorePreference);
mController = new MobileNetworkListController(mContext, mLifecycle);
}
@@ -78,6 +88,22 @@ public class MobileNetworkListControllerTest {
mController.onResume();
}
@Test
public void displayPreference_eSimNotSupported_addMoreLinkNotVisible() {
when(mEuiccManager.isEnabled()).thenReturn(false);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
assertThat(mAddMorePreference.isVisible()).isFalse();
}
@Test
public void displayPreference_eSimSupported_addMoreLinkIsVisible() {
when(mEuiccManager.isEnabled()).thenReturn(true);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
assertThat(mAddMorePreference.isVisible()).isTrue();
}
@Test
public void displayPreference_twoSubscriptions_correctlySetup() {
final SubscriptionInfo sub1 = createMockSubscription(1, "sub1");

View File

@@ -16,13 +16,9 @@
package com.android.settings.network;
import static android.telephony.TelephonyManager.MultiSimVariants.DSDS;
import static android.telephony.TelephonyManager.MultiSimVariants.UNKNOWN;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
@@ -37,7 +33,6 @@ import android.content.Intent;
import android.net.ConnectivityManager;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
@@ -64,8 +59,6 @@ public class MobileNetworkSummaryControllerTest {
@Mock
private Lifecycle mLifecycle;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private EuiccManager mEuiccManager;
@Mock
private PreferenceScreen mPreferenceScreen;
@@ -78,9 +71,7 @@ public class MobileNetworkSummaryControllerTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(Robolectric.setupActivity(Activity.class));
when(mContext.getSystemService(eq(TelephonyManager.class))).thenReturn(mTelephonyManager);
when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(UNKNOWN);
when(mEuiccManager.isEnabled()).thenReturn(true);
mController = new MobileNetworkSummaryController(mContext, mLifecycle);
@@ -97,7 +88,7 @@ public class MobileNetworkSummaryControllerTest {
@Test
public void isAvailable_wifiOnlyMode_notAvailable() {
ConnectivityManager cm = mock(ConnectivityManager.class);
final ConnectivityManager cm = mock(ConnectivityManager.class);
when(cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(cm);
assertThat(mController.isAvailable()).isFalse();
@@ -212,24 +203,7 @@ public class MobileNetworkSummaryControllerTest {
}
@Test
public void addButton_noSubscriptionsSingleSimMode_noAddClickListener() {
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(notNull());
}
@Test
public void addButton_oneSubscriptionSingleSimMode_noAddClickListener() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(notNull());
}
@Test
public void addButton_noSubscriptionsMultiSimModeNoEuiccMgr_noAddClickListener() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
public void addButton_noSubscriptionsNoEuiccMgr_noAddClickListener() {
when(mEuiccManager.isEnabled()).thenReturn(false);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
@@ -237,41 +211,43 @@ public class MobileNetworkSummaryControllerTest {
}
@Test
public void addButton_noSubscriptionsMultiSimMode_hasAddClickListenerAndPrefDisabled() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
mController.displayPreference(mPreferenceScreen);
mController.onResume();
assertThat(mPreference.isEnabled()).isFalse();
verify(mPreference, never()).setOnAddClickListener(isNull());
verify(mPreference).setOnAddClickListener(notNull());
}
@Test
public void addButton_oneSubscriptionMultiSimMode_hasAddClickListener() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
public void addButton_oneSubscriptionNoEuiccMgr_noAddClickListener() {
when(mEuiccManager.isEnabled()).thenReturn(false);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(notNull());
}
@Test
public void addButton_noSubscriptions_noAddClickListener() {
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(notNull());
}
@Test
public void addButton_oneSubscription_hasAddClickListener() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(isNull());
verify(mPreference).setOnAddClickListener(notNull());
}
@Test
public void addButton_twoSubscriptionsMultiSimMode_hasAddClickListener() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
public void addButton_twoSubscriptions_hasAddClickListener() {
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
final SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1, sub2));
mController.displayPreference(mPreferenceScreen);
mController.onResume();
verify(mPreference, never()).setOnAddClickListener(isNull());
verify(mPreference).setOnAddClickListener(notNull());
}
@Test
public void addButton_oneSubscriptionAirplaneModeTurnedOn_addButtonGetsDisabled() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
mController.displayPreference(mPreferenceScreen);
@@ -280,14 +256,13 @@ public class MobileNetworkSummaryControllerTest {
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
mController.onAirplaneModeChanged(true);
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isFalse();
}
@Test
public void onResume_oneSubscriptionAirplaneMode_isDisabled() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
@@ -296,7 +271,7 @@ public class MobileNetworkSummaryControllerTest {
assertThat(mPreference.isEnabled()).isFalse();
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isFalse();
}
@@ -318,7 +293,6 @@ public class MobileNetworkSummaryControllerTest {
@Test
public void onAirplaneModeChanged_oneSubscriptionAirplaneModeGetsTurnedOff_isEnabled() {
when(mTelephonyManager.getMultiSimConfiguration()).thenReturn(DSDS);
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1);
final SubscriptionInfo sub1 = mock(SubscriptionInfo.class);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(sub1));
@@ -332,7 +306,7 @@ public class MobileNetworkSummaryControllerTest {
assertThat(mPreference.isEnabled()).isTrue();
ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
final ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(eq(false));
verify(mPreference, atLeastOnce()).setAddWidgetEnabled(captor.capture());
assertThat(captor.getValue()).isTrue();

View File

@@ -22,7 +22,6 @@ import static android.app.slice.SliceItem.FORMAT_TEXT;
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.verify;
@@ -43,7 +42,6 @@ import androidx.slice.widget.SliceLiveData;
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.slices.CustomSliceManager;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -85,10 +83,6 @@ public class Enhanced4gLteSliceHelperTest {
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
CustomSliceManager manager = new CustomSliceManager(mContext);
when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
.thenReturn(manager);
//setup for SettingsSliceProvider tests
mProvider = spy(new SettingsSliceProvider());
doReturn(mContext).when(mProvider).getContext();

View File

@@ -110,6 +110,26 @@ public class BlockPreferenceControllerTest {
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfChannelNonDefault() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.systemApp = true;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_ifChannelDefault() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
mController.onResume(appRow, channel, null, null);
assertTrue(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfGroupNotBlockable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.UserManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class HighImportancePreferenceControllerTest {
private Context mContext;
@Mock
private NotificationManager mNm;
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettingsBase.ImportanceListener mImportanceListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private HighImportancePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new HighImportancePreferenceController(
mContext, mImportanceListener, mBackend));
}
@Test
public void testNoCrashIfNoOnResume() {
mController.isAvailable();
mController.updateState(mock(Preference.class));
}
@Test
public void testIsAvailable_notIfNull() {
mController.onResume(null, null, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_ifAppBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfChannelBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notForDefaultChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_DEFAULT);
mController.onResume(appRow, channel, null, null);
assertTrue(mController.isAvailable());
}
@Test
public void testUpdateState_disabledByAdmin() {
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
RestrictedLockUtils.EnforcedAdmin.class));
Preference pref = new RestrictedSwitchPreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_notConfigurable() {
String lockedId = "locked";
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.lockedChannelId = lockedId;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn(lockedId);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Preference pref = new RestrictedSwitchPreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_high() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertTrue(pref.isChecked());
}
@Test
public void testUpdateState_default() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertFalse(pref.isChecked());
}
@Test
public void onPreferenceChange() {
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, false);
assertEquals(IMPORTANCE_DEFAULT, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
}
}

View File

@@ -27,8 +27,11 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -36,12 +39,10 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.UserManager;
import android.text.TextUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.RestrictedListPreference;
import com.android.settingslib.RestrictedLockUtils;
import org.junit.Before;
@@ -95,20 +96,20 @@ public class ImportancePreferenceControllerTest {
}
@Test
public void testIsAvailable_notIfAppBlocked() {
public void testIsAvailable_ifAppBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
assertFalse(mController.isAvailable());
assertTrue(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfChannelBlocked() {
public void testIsAvailable_evenIfChannelBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
assertTrue(mController.isAvailable());
}
@Test
@@ -137,11 +138,10 @@ public class ImportancePreferenceControllerTest {
mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
RestrictedLockUtils.EnforcedAdmin.class));
Preference pref = new RestrictedListPreference(mContext, null);
Preference pref = new ImportancePreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
assertFalse(TextUtils.isEmpty(pref.getSummary()));
}
@Test
@@ -154,11 +154,10 @@ public class ImportancePreferenceControllerTest {
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Preference pref = new RestrictedListPreference(mContext, null);
Preference pref = new ImportancePreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
assertFalse(TextUtils.isEmpty(pref.getSummary()));
}
@Test
@@ -167,11 +166,12 @@ public class ImportancePreferenceControllerTest {
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Preference pref = new RestrictedListPreference(mContext, null);
ImportancePreference pref = mock(ImportancePreference.class);
mController.updateState(pref);
assertTrue(pref.isEnabled());
assertFalse(TextUtils.isEmpty(pref.getSummary()));
verify(pref, times(1)).setConfigurable(anyBoolean());
verify(pref, times(1)).setBlockable(anyBoolean());
verify(pref, times(1)).setImportance(IMPORTANCE_HIGH);
}
@Test
@@ -181,13 +181,12 @@ public class ImportancePreferenceControllerTest {
channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
RestrictedListPreference pref = new RestrictedListPreference(mContext, null);
ImportancePreference pref = new ImportancePreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
pref.setValue(String.valueOf(IMPORTANCE_HIGH));
mController.onPreferenceChange(pref, pref.getValue());
mController.onPreferenceChange(pref, IMPORTANCE_HIGH);
assertEquals(IMPORTANCE_HIGH, channel.getImportance());
assertNotNull(channel.getSound());
@@ -200,13 +199,12 @@ public class ImportancePreferenceControllerTest {
channel.setSound(null, Notification.AUDIO_ATTRIBUTES_DEFAULT);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
RestrictedListPreference pref = new RestrictedListPreference(mContext, null);
ImportancePreference pref = new ImportancePreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
pref.setValue(String.valueOf(IMPORTANCE_LOW));
mController.onPreferenceChange(pref, pref.getValue());
mController.onPreferenceChange(pref, IMPORTANCE_LOW);
assertEquals(IMPORTANCE_LOW, channel.getImportance());
assertNull(channel.getSound());

View File

@@ -0,0 +1,192 @@
/*
* 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.notification;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Switch;
import com.android.settings.R;
import com.android.settingslib.RestrictedLockUtils;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
@RunWith(RobolectricTestRunner.class)
public class ImportancePreferenceTest {
private Context mContext;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
}
private GradientDrawable getBackground(ImageButton button) {
return (GradientDrawable) ((LayerDrawable) button.getDrawable())
.findDrawableByLayerId(R.id.back);
}
@Test
public void createNewPreference_shouldSetLayout() {
final ImportancePreference preference = new ImportancePreference(mContext);
assertThat(preference.getLayoutResource()).isEqualTo(
R.layout.notif_importance_preference);
}
@Test
public void onBindViewHolder_hideBlockNonBlockable() {
final ImportancePreference preference = new ImportancePreference(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.setBlockable(false);
preference.setConfigurable(true);
preference.setImportance(IMPORTANCE_DEFAULT);
preference.onBindViewHolder(holder);
assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_hideNonSelectedNonConfigurable() {
final ImportancePreference preference = new ImportancePreference(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.setBlockable(true);
preference.setConfigurable(false);
preference.setImportance(IMPORTANCE_DEFAULT);
preference.onBindViewHolder(holder);
assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
assertThat(holder.itemView.findViewById(R.id.silence).getVisibility()).isEqualTo(View.GONE);
assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
.isEqualTo(View.VISIBLE);
// other button
preference.setImportance(IMPORTANCE_LOW);
holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.onBindViewHolder(holder);
assertThat(holder.itemView.findViewById(R.id.block).getVisibility()).isEqualTo(View.GONE);
assertThat(holder.itemView.findViewById(R.id.silence).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
.isEqualTo(View.GONE);
}
@Test
public void onBindViewHolder_selectButton() {
final ImportancePreference preference = new ImportancePreference(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.setBlockable(true);
preference.setConfigurable(true);
preference.setImportance(IMPORTANCE_DEFAULT);
ImageButton blockButton = (ImageButton) holder.findViewById(R.id.block_icon);
ImageButton silenceButton = (ImageButton) holder.findViewById(R.id.silence_icon);
ImageButton alertButton = (ImageButton) holder.findViewById(R.id.alert_icon);
preference.onBindViewHolder(holder);
// selected has full color background. others are transparent
assertThat(getBackground(alertButton).getColor().getColors()[0]).isNotEqualTo(
Color.TRANSPARENT);
assertThat(getBackground(silenceButton).getColor().getColors()[0]).isEqualTo(
Color.TRANSPARENT);
assertThat(getBackground(blockButton).getColor().getColors()[0]).isEqualTo(
Color.TRANSPARENT);
}
@Test
public void onClick_changesUICallsListener() {
final ImportancePreference preference = spy(new ImportancePreference(mContext));
final LayoutInflater inflater = LayoutInflater.from(mContext);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.setBlockable(true);
preference.setConfigurable(true);
preference.setImportance(IMPORTANCE_DEFAULT);
preference.onBindViewHolder(holder);
ImageButton blockButton = (ImageButton) holder.findViewById(R.id.block_icon);
ImageButton silenceButton = (ImageButton) holder.findViewById(R.id.silence_icon);
ImageButton alertButton = (ImageButton) holder.findViewById(R.id.alert_icon);
silenceButton.callOnClick();
// selected has full color background. others are transparent
assertThat(getBackground(silenceButton).getColor().getColors()[0]).isNotEqualTo(
Color.TRANSPARENT);
assertThat(getBackground(alertButton).getColor().getColors()[0]).isEqualTo(
Color.TRANSPARENT);
assertThat(getBackground(blockButton).getColor().getColors()[0]).isEqualTo(
Color.TRANSPARENT);
verify(preference, times(1)).callChangeListener(IMPORTANCE_LOW);
}
@Test
public void onBindViewHolder_allButtonsVisible() {
final ImportancePreference preference = new ImportancePreference(mContext);
final LayoutInflater inflater = LayoutInflater.from(mContext);
final PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(
inflater.inflate(R.layout.notif_importance_preference, null));
preference.setBlockable(true);
preference.setConfigurable(true);
preference.onBindViewHolder(holder);
assertThat(holder.itemView.findViewById(R.id.block).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(holder.itemView.findViewById(R.id.silence).getVisibility())
.isEqualTo(View.VISIBLE);
assertThat(holder.itemView.findViewById(R.id.alert).getVisibility())
.isEqualTo(View.VISIBLE);
}
}

View File

@@ -0,0 +1,199 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.UserManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class MinImportancePreferenceControllerTest {
private Context mContext;
@Mock
private NotificationManager mNm;
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationSettingsBase.ImportanceListener mImportanceListener;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private MinImportancePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
ShadowApplication shadowApplication = ShadowApplication.getInstance();
shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm);
shadowApplication.setSystemService(Context.USER_SERVICE, mUm);
mContext = RuntimeEnvironment.application;
mController = spy(new MinImportancePreferenceController(
mContext, mImportanceListener, mBackend));
}
@Test
public void testNoCrashIfNoOnResume() {
mController.isAvailable();
mController.updateState(mock(Preference.class));
}
@Test
public void testIsAvailable_notIfNull() {
mController.onResume(null, null, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_ifAppBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfChannelBlocked() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_NONE);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notForDefaultChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
mController.onResume(appRow, channel, null, null);
assertTrue(mController.isAvailable());
}
@Test
public void testUpdateState_disabledByAdmin() {
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
mController.onResume(new NotificationBackend.AppRow(), channel, null, mock(
RestrictedLockUtils.EnforcedAdmin.class));
Preference pref = new RestrictedSwitchPreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_notConfigurable() {
String lockedId = "locked";
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.lockedChannelId = lockedId;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn(lockedId);
when(channel.getImportance()).thenReturn(IMPORTANCE_LOW);
mController.onResume(appRow, channel, null, null);
Preference pref = new RestrictedSwitchPreference(mContext, null);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_min() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertTrue(pref.isChecked());
}
@Test
public void testUpdateState_low() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_LOW);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertFalse(pref.isChecked());
}
@Test
public void onPreferenceChange() {
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
mController.onResume(new NotificationBackend.AppRow(), channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext, null);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, true);
assertEquals(IMPORTANCE_MIN, channel.getImportance());
verify(mImportanceListener, times(1)).onImportanceChanged();
}
}

View File

@@ -218,6 +218,20 @@ public class NotificationPreferenceControllerTest {
assertTrue(mController.isChannelConfigurable());
}
@Test
public void testIsConfigurable_appLevel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.lockedChannelId = "something";
appRow.lockedImportance = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
assertFalse(mController.isChannelConfigurable());
appRow.lockedImportance = false;
mController.onResume(appRow, mock(NotificationChannel.class), null, null);
assertTrue(mController.isChannelConfigurable());
}
@Test
public void testIsChannelBlockable_nonSystemAppsBlockable() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();

View File

@@ -135,7 +135,6 @@ public class SettingsSliceProviderTest {
mProvider.mSliceWeakDataCache = new HashMap<>();
mProvider.mSliceDataCache = new HashMap<>();
mProvider.mSlicesDatabaseAccessor = new SlicesDatabaseAccessor(mContext);
mProvider.mCustomSliceManager = new CustomSliceManager(mContext);
when(mProvider.getContext()).thenReturn(mContext);
SlicesDatabaseHelper.getInstance(mContext).setIndexedState();

View File

@@ -19,14 +19,12 @@ package com.android.settings.slices;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.settings.SettingsEnums;
import android.app.slice.Slice;
@@ -82,9 +80,6 @@ public class SliceBroadcastReceiverTest {
mSearchFeatureProvider = new SearchFeatureProviderImpl();
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
mFakeFeatureFactory.searchFeatureProvider = mSearchFeatureProvider;
CustomSliceManager manager = new CustomSliceManager(mContext);
when(mFakeFeatureFactory.slicesFeatureProvider.getCustomSliceManager(any()))
.thenReturn(manager);
}
@After

View File

@@ -55,6 +55,7 @@ public class SlicesDatabaseHelperTest {
@After
public void cleanUp() {
DatabaseTestUtils.clearDb(mContext);
mDatabase.close();
}
@Test

View File

@@ -19,8 +19,6 @@ package com.android.settings.slices;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -39,30 +37,36 @@ import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SpecialCaseSliceManagerTest {
private Context mContext;
private final String FAKE_PARAMETER_KEY = "fake_parameter_key";
private final String FAKE_PARAMETER_VALUE = "fake_value";
private CustomSliceManager mCustomSliceManager;
private Context mContext;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mCustomSliceManager = spy(new CustomSliceManager(mContext));
CustomSliceRegistry.sUriToSlice.clear();
CustomSliceRegistry.sUriToSlice.put(FakeSliceable.URI, FakeSliceable.class);
}
@Test
public void getSliceableFromUri_returnsCorrectObject() {
final CustomSliceable sliceable = mCustomSliceManager.getSliceableFromUri(
FakeSliceable.URI);
final CustomSliceable sliceable = CustomSliceable.createInstance(
mContext, CustomSliceRegistry.getSliceClassByUri(FakeSliceable.URI));
assertThat(sliceable).isInstanceOf(FakeSliceable.class);
}
@Test
public void getSliceableFromIntentAction_returnsCorrectObject() {
final CustomSliceable sliceable =
mCustomSliceManager.getSliceableFromIntentAction(FakeSliceable.URI.toString());
public void getSliceableFromUriWithParameter_returnsCorrectObject() {
final Uri parameterUri = FakeSliceable.URI
.buildUpon()
.clearQuery()
.appendQueryParameter(FAKE_PARAMETER_KEY, FAKE_PARAMETER_VALUE)
.build();
final CustomSliceable sliceable = CustomSliceable.createInstance(
mContext, CustomSliceRegistry.getSliceClassByUri(parameterUri));
assertThat(sliceable).isInstanceOf(FakeSliceable.class);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.testutils.shadow;
import android.content.Context;
import com.android.settings.wifi.calling.DisclaimerItemFactory;
import com.android.settings.wifi.calling.DisclaimerItem;
import java.util.List;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
@Implements(DisclaimerItemFactory.class)
public final class ShadowDisclaimerItemFactory {
private static List<DisclaimerItem> sMockDisclaimerItemList;
public static void setDisclaimerItemList(List<DisclaimerItem> list) {
sMockDisclaimerItemList = list;
}
@Implementation
public static List<DisclaimerItem> create(Context context, int subId) {
return sMockDisclaimerItemList;
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
.DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_TITLE;
import static com.android.settings.wifi.calling.DisclaimerItemListAdapter
.DisclaimerItemViewHolder.ID_DISCLAIMER_ITEM_DESCRIPTION;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.android.settings.R;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class DisclaimerItemListAdapterTest {
private static final int ITEM_POSITION = 0;
private static final int DISCLAIMER_TITLE_STRING_ID = 0;
private static final int DISCLAIMER_MESSAGE_STRING_ID = 1;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private TextView mTestView;
@Mock
private TextView mDescView;
@Mock
private View mView;
@Mock
private ViewGroup mViewGroup;
@Mock
private Context mContext;
private MockDisclaimerItem mDisclaimerItem;
private DisclaimerItemListAdapter mDisclaimerItemListAdapter;
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mDisclaimerItem = spy(new MockDisclaimerItem(mContext, 0 /* subId */));
mDisclaimerItemList.add(mDisclaimerItem);
when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
when(mViewGroup.getContext()).thenReturn(mContext);
when(mViewGroup.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).thenReturn(
mLayoutInflater);
when(mView.findViewById(ID_DISCLAIMER_ITEM_TITLE)).thenReturn(mTestView);
when(mView.findViewById(ID_DISCLAIMER_ITEM_DESCRIPTION)).thenReturn(mDescView);
}
@Test
public void onBindViewHolder_haveItem_shouldSetText() {
final DisclaimerItemListAdapter.DisclaimerItemViewHolder viewHolder =
new DisclaimerItemListAdapter.DisclaimerItemViewHolder(mView);
mDisclaimerItemListAdapter = new DisclaimerItemListAdapter(mDisclaimerItemList);
mDisclaimerItemListAdapter.onCreateViewHolder(mViewGroup, 0 /* viewType */);
mDisclaimerItemListAdapter.onBindViewHolder(viewHolder, ITEM_POSITION);
// Check the text is set when the DisclaimerItem exists.
verify(viewHolder.titleView).setText(DISCLAIMER_TITLE_STRING_ID);
verify(viewHolder.descriptionView).setText(DISCLAIMER_MESSAGE_STRING_ID);
}
private class MockDisclaimerItem extends DisclaimerItem {
MockDisclaimerItem(Context context, int subId) {
super(context, subId);
}
@Override
protected int getTitleId() {
return DISCLAIMER_TITLE_STRING_ID;
}
@Override
protected int getMessageId() {
return DISCLAIMER_MESSAGE_STRING_ID;
}
@Override
protected String getName() {
return "MockDisclaimerItem";
}
@Override
protected String getPrefKey() {
return "mock_pref_key";
}
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.wifi.calling;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowDisclaimerItemFactory;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowDisclaimerItemFactory.class)
public class WifiCallingDisclaimerFragmentTest {
@Mock
private Activity mActivity;
@Mock
private DisclaimerItem mDisclaimerItem;
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private View mView;
@Mock
private ViewGroup mViewGroup;
@Mock
private Button mAgreeButton;
@Mock
private Button mDisagreeButton;
@Mock
private RecyclerView mRecyclerView;
@Captor
ArgumentCaptor<OnClickListener> mOnClickListenerCaptor;
@Captor
ArgumentCaptor<OnScrollListener> mOnScrollListenerCaptor;
private WifiCallingDisclaimerFragment mFragment;
private List<DisclaimerItem> mDisclaimerItemList = new ArrayList<>();
private List<DisclaimerItem> mEmptyDisclaimerItemList = new ArrayList<>();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivity = Robolectric.setupActivity(Activity.class);
mFragment = spy(new WifiCallingDisclaimerFragment());
doReturn(mActivity).when(mFragment).getActivity();
when(mLayoutInflater.inflate(anyInt(), anyObject(), anyBoolean())).thenReturn(mView);
when(mView.findViewById(R.id.agree_button)).thenReturn(mAgreeButton);
when(mView.findViewById(R.id.disagree_button)).thenReturn(mDisagreeButton);
when(mView.findViewById(R.id.disclaimer_item_list)).thenReturn(mRecyclerView);
when(mView.getId()).thenReturn(R.id.agree_button);
mOnScrollListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
doNothing().when(mRecyclerView).addOnScrollListener(mOnScrollListenerCaptor.capture());
mOnClickListenerCaptor = ArgumentCaptor.forClass(OnClickListener.class);
doNothing().when(mAgreeButton).setOnClickListener(mOnClickListenerCaptor.capture());
mDisclaimerItemList.add(mDisclaimerItem);
}
@Test
public void onCreate_notHaveItem_shouldFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mEmptyDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
// Check the fragment is finished when the DisclaimerItemList is empty.
verify(mFragment).finish(Activity.RESULT_OK);
}
@Test
public void onCreate_haveItem_shouldNotFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
// Check the fragment is not finished when the DisclaimerItemList is not empty.
verify(mFragment, never()).finish(Activity.RESULT_OK);
}
@Test
public void onScrolled_canNotScroll_shouldEnableAgreeButton() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
when(mRecyclerView.canScrollVertically(1)).thenReturn(false);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
// Check the agreeButton is enabled when the view is scrolled to the bottom end.
verify(mAgreeButton).setEnabled(true);
// Check the OnScrollListener is removed when the view is scrolled to the bottom end.
verify(mRecyclerView).removeOnScrollListener(any());
}
@Test
public void onScrolled_canScroll_shouldNotEnableAgreeButton() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
when(mRecyclerView.canScrollVertically(1)).thenReturn(true);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnScrollListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 0);
// Check the agreeButton is not enabled when the view is not scrolled to the bottom end.
verify(mAgreeButton, never()).setEnabled(anyBoolean());
// Check the OnScrollListener is not removed when the view is not scrolled to
// the bottom end.
verify(mRecyclerView, never()).removeOnScrollListener(any());
}
@Test
public void onClick_agreeButton_shouldFinishFragment() {
ShadowDisclaimerItemFactory.setDisclaimerItemList(mDisclaimerItemList);
mFragment.onCreate(null /* savedInstanceState */);
mFragment.onCreateView(mLayoutInflater, mViewGroup, null /* savedInstanceState */);
mOnClickListenerCaptor.getValue().onClick(mAgreeButton);
// Check the onAgreed callback is called when "CONTINUE" button is clicked.
verify(mDisclaimerItem).onAgreed();
// Check the WFC disclaimer fragment is finished when "CONTINUE" button is clicked.
verify(mFragment).finish(Activity.RESULT_OK);
}
}

View File

@@ -16,11 +16,14 @@
package com.android.settings.wifi.calling;
import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
@@ -31,6 +34,8 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -41,18 +46,22 @@ import android.telephony.ims.ProvisioningManager;
import android.view.View;
import android.widget.TextView;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.ToggleSwitch;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@@ -63,6 +72,8 @@ import org.robolectric.util.ReflectionHelpers;
public class WifiCallingSettingsForSubTest {
private static final String BUTTON_WFC_MODE = "wifi_calling_mode";
private static final String BUTTON_WFC_ROAMING_MODE = "wifi_calling_roaming_mode";
private static final String TEST_EMERGENCY_ADDRESS_CARRIER_APP =
"com.android.settings/.wifi.calling.TestEmergencyAddressCarrierApp";
private TestFragment mFragment;
private Context mContext;
@@ -70,6 +81,7 @@ public class WifiCallingSettingsForSubTest {
private final PersistableBundle mBundle = new PersistableBundle();
@Mock private static CarrierConfigManager sCarrierConfigManager;
@Mock private CarrierConfigManager mMockConfigManager;
@Mock private ImsManager mImsManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private PreferenceScreen mPreferenceScreen;
@@ -80,6 +92,7 @@ public class WifiCallingSettingsForSubTest {
@Mock private ImsConfig mImsConfig;
@Mock private ListWithEntrySummaryPreference mButtonWfcMode;
@Mock private ListWithEntrySummaryPreference mButtonWfcRoamingMode;
@Mock private Preference mUpdateAddress;
@Before
public void setUp() throws Exception {
@@ -121,6 +134,11 @@ public class WifiCallingSettingsForSubTest {
doReturn(mBundle).when(sCarrierConfigManager).getConfigForSubId(anyInt());
setDefaultCarrierConfigValues();
doReturn(sCarrierConfigManager).when(mActivity).getSystemService(
CarrierConfigManager.class);
doReturn(mContext.getResources()).when(mFragment).getResourcesForSubId();
doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt());
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onActivityCreated(null);
@@ -131,6 +149,9 @@ public class WifiCallingSettingsForSubTest {
CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_MODE_BOOL, true);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true);
mBundle.putString(
CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING,
TEST_EMERGENCY_ADDRESS_CARRIER_APP);
}
@Test
@@ -259,6 +280,61 @@ public class WifiCallingSettingsForSubTest {
eq(true));
}
@Test
public void onSwitchChanged_enableSetting_shouldLaunchWfcDisclaimerFragment() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
mFragment.onSwitchChanged(null, true);
// Check the WFC disclaimer fragment is launched.
verify(mFragment).startActivityForResult(intentCaptor.capture(),
eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER));
Intent intent = intentCaptor.getValue();
assertThat(intent.getStringExtra(EXTRA_SHOW_FRAGMENT))
.isEqualTo(WifiCallingDisclaimerFragment.class.getName());
}
@Test
public void onActivityResult_finishWfcDisclaimerFragment_shouldLaunchCarrierActivity() {
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
// Emulate the WfcDisclaimerActivity finish.
mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_DISCLAIMER,
Activity.RESULT_OK, null);
// Check the WFC emergency address activity is launched.
verify(mFragment).startActivityForResult(intentCaptor.capture(),
eq(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS));
Intent intent = intentCaptor.getValue();
assertEquals(intent.getComponent(), ComponentName.unflattenFromString(
TEST_EMERGENCY_ADDRESS_CARRIER_APP));
}
@Test
public void onActivityResult_finishCarrierActivity_shouldShowWfcPreference() {
ReflectionHelpers.setField(mFragment, "mButtonWfcMode", mButtonWfcMode);
ReflectionHelpers.setField(mFragment, "mButtonWfcRoamingMode", mButtonWfcRoamingMode);
ReflectionHelpers.setField(mFragment, "mUpdateAddress", mUpdateAddress);
mFragment.onActivityResult(WifiCallingSettingsForSub.REQUEST_CHECK_WFC_EMERGENCY_ADDRESS,
Activity.RESULT_OK, null);
// Check the WFC preferences is added.
verify(mPreferenceScreen).addPreference(mButtonWfcMode);
verify(mPreferenceScreen).addPreference(mButtonWfcRoamingMode);
verify(mPreferenceScreen).addPreference(mUpdateAddress);
// Check the WFC enable request.
verify(mImsManager).setWfcSetting(true);
}
@Test
public void onSwitchChanged_disableSetting_shouldNotLaunchWfcDisclaimerFragment() {
mFragment.onSwitchChanged(null, false);
// Check the WFC disclaimer fragment is not launched.
verify(mFragment, never()).startActivityForResult(any(Intent.class), anyInt());
}
protected class TestFragment extends WifiCallingSettingsForSub {
@Override
protected Object getSystemService(final String name) {

View File

@@ -22,7 +22,6 @@ import static android.app.slice.SliceItem.FORMAT_TEXT;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
@@ -48,7 +47,6 @@ import androidx.slice.widget.SliceLiveData;
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.slices.CustomSliceManager;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
@@ -99,9 +97,6 @@ public class WifiCallingSliceHelperTest {
mFeatureFactory = FakeFeatureFactory.setupForTest();
mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider();
CustomSliceManager manager = new CustomSliceManager(mContext);
when(mSlicesFeatureProvider.getCustomSliceManager(any(Context.class)))
.thenReturn(manager);
mWfcSliceHelper = new FakeWifiCallingSliceHelper(mContext);

View File

@@ -778,9 +778,9 @@ public class WifiDetailPreferenceControllerTest {
mockWifiConfig.networkId = 5;
when(mockWifiConfig.isPasspoint()).thenReturn(true);
spyController.displayPreference(mockScreen);
FeatureFlagPersistent.setEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2, true);
spyController.displayPreference(mockScreen);
mForgetClickListener.getValue().onClick(null);
verify(mockWifiManager, times(0)).removePasspointConfiguration(mockWifiConfig.FQDN);

View File

@@ -36,6 +36,8 @@ import androidx.slice.widget.SliceLiveData;
import com.android.settings.R;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SlicesFeatureProviderImpl;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Test;
@@ -52,11 +54,15 @@ public class ContextualWifiSliceTest {
private ContentResolver mResolver;
private WifiManager mWifiManager;
private ContextualWifiSlice mWifiSlice;
private FakeFeatureFactory mFeatureFactory;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
mResolver = mock(ContentResolver.class);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mFeatureFactory.slicesFeatureProvider = new SlicesFeatureProviderImpl();
mFeatureFactory.slicesFeatureProvider.newUiSession();
doReturn(mResolver).when(mContext).getContentResolver();
mWifiManager = mContext.getSystemService(WifiManager.class);
@@ -65,10 +71,28 @@ public class ContextualWifiSliceTest {
mWifiManager.setWifiEnabled(true);
mWifiSlice = new ContextualWifiSlice(mContext);
mWifiSlice.sPreviouslyDisplayed = false;
}
@Test
public void getWifiSlice_hasActiveConnection_shouldReturnNull() {
mWifiSlice.sPreviouslyDisplayed = false;
final WifiConfiguration config = new WifiConfiguration();
config.SSID = "123";
mWifiManager.connect(config, null /* listener */);
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;
mWifiSlice.sActiveUiSession = ~mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
final WifiConfiguration config = new WifiConfiguration();
config.SSID = "123";
mWifiManager.connect(config, null /* listener */);
@@ -80,7 +104,8 @@ public class ContextualWifiSliceTest {
@Test
public void getWifiSlice_previousDisplayed_hasActiveConnection_shouldHaveTitleAndToggle() {
mWifiSlice.mPreviouslyDisplayed = true;
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
mWifiSlice.sPreviouslyDisplayed = true;
final WifiConfiguration config = new WifiConfiguration();
config.SSID = "123";
mWifiManager.connect(config, null /* listener */);
@@ -101,7 +126,8 @@ public class ContextualWifiSliceTest {
@Test
public void getWifiSlice_contextualWifiSlice_shouldReturnContextualWifiSliceUri() {
mWifiSlice.mPreviouslyDisplayed = true;
mWifiSlice.sActiveUiSession = mFeatureFactory.slicesFeatureProvider.getUiSessionToken();
mWifiSlice.sPreviouslyDisplayed = true;
final Slice wifiSlice = mWifiSlice.getSlice();