Snap for 5289249 from 175b7b9256 to qt-release

Change-Id: I383863d634137a3a802541cd4e0aeb19b96234e8
This commit is contained in:
android-build-team Robot
2019-02-07 04:09:54 +00:00
32 changed files with 1535 additions and 232 deletions

View File

@@ -1246,6 +1246,17 @@
android:value="true" />
</activity>
<activity android:name="Settings$PrivacyDashboardActivity"
android:label="@string/privacy_dashboard_title"
android:icon="@drawable/ic_settings_privacy">
<intent-filter>
<action android:name="android.settings.PRIVACY_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.privacy.PrivacyDashboardFragment" />
</activity>
<activity android:name="SetFullBackupPassword"
android:label="@string/local_backup_password_title"
android:exported="false" />
@@ -2792,10 +2803,6 @@
<activity android:name=".backup.UserBackupSettingsActivity"
android:label="@string/privacy_settings_title"
android:icon="@drawable/ic_settings_backup">
<intent-filter android:priority="1">
<action android:name="android.settings.PRIVACY_SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:clickable="false"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/icon_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="56dp"
android:gravity="start|center_vertical"
android:orientation="horizontal"
android:paddingEnd="12dp"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<com.android.internal.widget.PreferenceImageView
android:id="@android:id/icon"
android:layout_width="24dp"
android:layout_height="24dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@android:id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:textAppearance="@*android:style/TextAppearance.Material.Subhead"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="marquee"
android:fadingEdge="horizontal"/>
<LinearLayout
android:id="@android:id/widget_frame"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="vertical"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.android.settings.accessibility.BalanceSeekBar
android:id="@*android:id/seekbar"
android:layout_gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/left_text"
android:text="@string/accessibility_toggle_master_balance_left_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:textAlignment="viewStart" />
<TextView
android:id="@+id/right_text"
android:text="@string/accessibility_toggle_master_balance_right_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:textAlignment="viewEnd" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -1,191 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- Whether or not the dock settings are to be displayed for this device when docked -->
<bool name="has_dock_settings">false</bool>
<!-- Whether there is a boot sounds checkbox -->
<bool name="has_boot_sounds">false</bool>
<!-- Whether there is a silent mode checkbox -->
<bool name="has_silent_mode">true</bool>
<!-- Display additional System Update menu if true -->
<bool name="config_additional_system_update_setting_enable">false</bool>
<!-- Whether the bluetooth activation confirmation dialogs should be auto dismissed.
Can be overridden for specific product builds. -->
<bool name="auto_confirm_bluetooth_activation_dialog">false</bool>
<!-- Whether the device name is shown in About device or not -->
<bool name="config_show_device_name">true</bool>
<!-- Whether to show a preference item for the manual in About phone -->
<bool name="config_show_manual">false</bool>
<!-- Whether to show a preference item for regulatory information in About phone -->
<bool name="config_show_regulatory_info">false</bool>
<!-- Whether to show a preference item for mobile plan -->
<bool name="config_show_mobile_plan">true</bool>
<!-- Whether none security option is hide or not (country specific). -->
<bool name="config_hide_none_security_option">false</bool>
<!-- Whether swipe security option is hidden or not -->
<bool name="config_hide_swipe_security_option">false</bool>
<!--Whether help links are defined. -->
<bool name="config_has_help">false</bool>
<!-- Whether Wi-Fi settings should be shown or not.
This also controls whether Wi-fi related sub-settings (e.g. Wi-Fi preferences) will
surface in search results or not.-->
<bool name="config_show_wifi_settings">true</bool>
<!-- Whether toggle_airplane is available or not. -->
<bool name="config_show_toggle_airplane">true</bool>
<!-- Whether private_dns_settings is available or not. -->
<bool name="config_show_private_dns_settings">true</bool>
<!-- Whether memory from app_info_settings is available or not. -->
<bool name="config_show_app_info_settings_memory">false</bool>
<!-- Whether battery from app_info_settings is available or not. -->
<bool name="config_show_app_info_settings_battery">true</bool>
<!-- Whether location mode is available or not. -->
<bool name="config_location_mode_available">true</bool>
<!-- Whether location scanning is available or not. -->
<bool name="config_show_location_scanning">true</bool>
<!-- Whether high_power_apps should be shown or not. -->
<bool name="config_show_high_power_apps">true</bool>
<!-- Whether media_volume should be shown or not. -->
<bool name="config_show_media_volume">true</bool>
<!-- Whether alarm_volume should be shown or not. -->
<bool name="config_show_alarm_volume">true</bool>
<!-- Whether call_volume should be shown or not. -->
<bool name="config_show_call_volume">true</bool>
<!-- Whether notification_volume should be shown or not. -->
<bool name="config_show_notification_volume">true</bool>
<!-- Whether notification_ringtone should be shown or not. -->
<bool name="config_show_notification_ringtone">true</bool>
<!-- Whether screen_locking_sounds should be shown or not. -->
<bool name="config_show_screen_locking_sounds">true</bool>
<!-- Whether charging_sounds should be shown or not. -->
<bool name="config_show_charging_sounds">true</bool>
<!-- Whether touch_sounds should be shown or not. -->
<bool name="config_show_touch_sounds">true</bool>
<!-- Whether encryption_and_credentials_encryption_status should be shown or not. -->
<bool name="config_show_encryption_and_credentials_encryption_status">true</bool>
<!-- Whether premium_sms should be shown or not. -->
<bool name="config_show_premium_sms">true</bool>
<!-- Whether data_saver should be shown or not. -->
<bool name="config_show_data_saver">true</bool>
<!-- Whether enabled_vr_listeners should be shown or not. -->
<bool name="config_show_enabled_vr_listeners">true</bool>
<!-- Whether phone_language should be shown or not. -->
<bool name="config_show_phone_language">true</bool>
<!-- Whether virtual_keyboard_pref should be shown or not. -->
<bool name="config_show_virtual_keyboard_pref">true</bool>
<!-- Whether physical_keyboard_pref should be shown or not. -->
<bool name="config_show_physical_keyboard_pref">true</bool>
<!-- Whether spellcheckers_settings should be shown or not. -->
<bool name="config_show_spellcheckers_settings">true</bool>
<!-- Whether tts_settings_summary should be shown or not. -->
<bool name="config_show_tts_settings_summary">true</bool>
<!-- Whether pointer_speed should be shown or not. -->
<bool name="config_show_pointer_speed">true</bool>
<!-- Whether vibrate_input_devices should be shown or not. -->
<bool name="config_show_vibrate_input_devices">true</bool>
<!-- Whether manage_device_admin should be shown or not. -->
<bool name="config_show_manage_device_admin">true</bool>
<!-- Whether unlock_set_or_change should be shown or not. -->
<bool name="config_show_unlock_set_or_change">true</bool>
<!-- Whether screen_pinning_settings should be shown or not. -->
<bool name="config_show_screen_pinning_settings">true</bool>
<!-- Whether manage_trust_agents should be shown or not. -->
<bool name="config_show_manage_trust_agents">true</bool>
<!-- Whether show_password should be shown or not. -->
<bool name="config_show_show_password">true</bool>
<!-- Whether trust_agent_click_intent should be shown or not. -->
<bool name="config_show_trust_agent_click_intent">true</bool>
<!-- Whether wallpaper attribution should be shown or not. -->
<bool name="config_show_wallpaper_attribution">true</bool>
<!-- Whether default_home should be shown or not. -->
<bool name="config_show_default_home">true</bool>
<!-- Whether assist_and_voice_input should be shown or not. -->
<bool name="config_show_assist_and_voice_input">true</bool>
<!-- Whether reset_dashboard should be shown or not. -->
<bool name="config_show_reset_dashboard">true</bool>
<!-- Whether system_update_settings should be shown or not. -->
<bool name="config_show_system_update_settings">true</bool>
<!-- Whether device_model should be shown or not. -->
<bool name="config_show_device_model">true</bool>
<!-- Whether top_level_battery should be shown or not. -->
<bool name="config_show_top_level_battery">true</bool>
<!-- Whether top_level_connected_devices should be shown or not. -->
<bool name="config_show_top_level_connected_devices">true</bool>
<!-- Whether top_level_display should be shown or not. -->
<bool name="config_show_top_level_display">true</bool>
<!-- Whether wifi_ip_address should be shown or not. -->
<bool name="config_show_wifi_ip_address">true</bool>
<!-- Whether wifi_mac_address should be shown or not. -->
<bool name="config_show_wifi_mac_address">true</bool>
<!-- Whether to disable "Uninstall Updates" menu item for System apps or not. -->
<bool name="config_disable_uninstall_update">false</bool>
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">true</bool>
</resources>

View File

@@ -15,9 +15,6 @@
-->
<resources>
<color name="black">#000</color>
<color name="red">#F00</color>
<color name="blue">#00F</color>
<color name="bluetooth_dialog_text_color">#8a000000</color>

View File

@@ -179,4 +179,178 @@
<!-- App intent -->
<string name="config_account_intent_uri" translatable="false"></string>
<!-- Whether or not the dock settings are to be displayed for this device when docked -->
<bool name="has_dock_settings">false</bool>
<!-- Whether there is a boot sounds checkbox -->
<bool name="has_boot_sounds">false</bool>
<!-- Whether there is a silent mode checkbox -->
<bool name="has_silent_mode">true</bool>
<!-- Display additional System Update menu if true -->
<bool name="config_additional_system_update_setting_enable">false</bool>
<!-- Whether the bluetooth activation confirmation dialogs should be auto dismissed.
Can be overridden for specific product builds. -->
<bool name="auto_confirm_bluetooth_activation_dialog">false</bool>
<!-- Whether the device name is shown in About device or not -->
<bool name="config_show_device_name">true</bool>
<!-- Whether to show a preference item for the manual in About phone -->
<bool name="config_show_manual">false</bool>
<!-- Whether to show a preference item for regulatory information in About phone -->
<bool name="config_show_regulatory_info">false</bool>
<!-- Whether to show a preference item for mobile plan -->
<bool name="config_show_mobile_plan">true</bool>
<!-- Whether none security option is hide or not (country specific). -->
<bool name="config_hide_none_security_option">false</bool>
<!-- Whether swipe security option is hidden or not -->
<bool name="config_hide_swipe_security_option">false</bool>
<!--Whether help links are defined. -->
<bool name="config_has_help">false</bool>
<!-- Whether Wi-Fi settings should be shown or not.
This also controls whether Wi-fi related sub-settings (e.g. Wi-Fi preferences) will
surface in search results or not.-->
<bool name="config_show_wifi_settings">true</bool>
<!-- Whether toggle_airplane is available or not. -->
<bool name="config_show_toggle_airplane">true</bool>
<!-- Whether private_dns_settings is available or not. -->
<bool name="config_show_private_dns_settings">true</bool>
<!-- Whether memory from app_info_settings is available or not. -->
<bool name="config_show_app_info_settings_memory">false</bool>
<!-- Whether battery from app_info_settings is available or not. -->
<bool name="config_show_app_info_settings_battery">true</bool>
<!-- Whether location mode is available or not. -->
<bool name="config_location_mode_available">true</bool>
<!-- Whether location scanning is available or not. -->
<bool name="config_show_location_scanning">true</bool>
<!-- Whether high_power_apps should be shown or not. -->
<bool name="config_show_high_power_apps">true</bool>
<!-- Whether media_volume should be shown or not. -->
<bool name="config_show_media_volume">true</bool>
<!-- Whether alarm_volume should be shown or not. -->
<bool name="config_show_alarm_volume">true</bool>
<!-- Whether call_volume should be shown or not. -->
<bool name="config_show_call_volume">true</bool>
<!-- Whether notification_volume should be shown or not. -->
<bool name="config_show_notification_volume">true</bool>
<!-- Whether notification_ringtone should be shown or not. -->
<bool name="config_show_notification_ringtone">true</bool>
<!-- Whether screen_locking_sounds should be shown or not. -->
<bool name="config_show_screen_locking_sounds">true</bool>
<!-- Whether charging_sounds should be shown or not. -->
<bool name="config_show_charging_sounds">true</bool>
<!-- Whether touch_sounds should be shown or not. -->
<bool name="config_show_touch_sounds">true</bool>
<!-- Whether encryption_and_credentials_encryption_status should be shown or not. -->
<bool name="config_show_encryption_and_credentials_encryption_status">true</bool>
<!-- Whether premium_sms should be shown or not. -->
<bool name="config_show_premium_sms">true</bool>
<!-- Whether data_saver should be shown or not. -->
<bool name="config_show_data_saver">true</bool>
<!-- Whether enabled_vr_listeners should be shown or not. -->
<bool name="config_show_enabled_vr_listeners">true</bool>
<!-- Whether phone_language should be shown or not. -->
<bool name="config_show_phone_language">true</bool>
<!-- Whether virtual_keyboard_pref should be shown or not. -->
<bool name="config_show_virtual_keyboard_pref">true</bool>
<!-- Whether physical_keyboard_pref should be shown or not. -->
<bool name="config_show_physical_keyboard_pref">true</bool>
<!-- Whether spellcheckers_settings should be shown or not. -->
<bool name="config_show_spellcheckers_settings">true</bool>
<!-- Whether tts_settings_summary should be shown or not. -->
<bool name="config_show_tts_settings_summary">true</bool>
<!-- Whether pointer_speed should be shown or not. -->
<bool name="config_show_pointer_speed">true</bool>
<!-- Whether vibrate_input_devices should be shown or not. -->
<bool name="config_show_vibrate_input_devices">true</bool>
<!-- Whether manage_device_admin should be shown or not. -->
<bool name="config_show_manage_device_admin">true</bool>
<!-- Whether unlock_set_or_change should be shown or not. -->
<bool name="config_show_unlock_set_or_change">true</bool>
<!-- Whether screen_pinning_settings should be shown or not. -->
<bool name="config_show_screen_pinning_settings">true</bool>
<!-- Whether manage_trust_agents should be shown or not. -->
<bool name="config_show_manage_trust_agents">true</bool>
<!-- Whether show_password should be shown or not. -->
<bool name="config_show_show_password">true</bool>
<!-- Whether trust_agent_click_intent should be shown or not. -->
<bool name="config_show_trust_agent_click_intent">true</bool>
<!-- Whether wallpaper attribution should be shown or not. -->
<bool name="config_show_wallpaper_attribution">true</bool>
<!-- Whether default_home should be shown or not. -->
<bool name="config_show_default_home">true</bool>
<!-- Whether assist_and_voice_input should be shown or not. -->
<bool name="config_show_assist_and_voice_input">true</bool>
<!-- Whether reset_dashboard should be shown or not. -->
<bool name="config_show_reset_dashboard">true</bool>
<!-- Whether system_update_settings should be shown or not. -->
<bool name="config_show_system_update_settings">true</bool>
<!-- Whether device_model should be shown or not. -->
<bool name="config_show_device_model">true</bool>
<!-- Whether top_level_battery should be shown or not. -->
<bool name="config_show_top_level_battery">true</bool>
<!-- Whether top_level_connected_devices should be shown or not. -->
<bool name="config_show_top_level_connected_devices">true</bool>
<!-- Whether top_level_display should be shown or not. -->
<bool name="config_show_top_level_display">true</bool>
<!-- Whether wifi_ip_address should be shown or not. -->
<bool name="config_show_wifi_ip_address">true</bool>
<!-- Whether wifi_mac_address should be shown or not. -->
<bool name="config_show_wifi_mac_address">true</bool>
<!-- Whether to disable "Uninstall Updates" menu item for System apps or not. -->
<bool name="config_disable_uninstall_update">false</bool>
<!-- Whether or not extra preview panels should be used for screen zoom setting. -->
<bool name="config_enable_extra_screen_zoom_preview">true</bool>
</resources>

View File

@@ -38,6 +38,9 @@
<dimen name="volume_seekbar_side_margin">8dip</dimen>
<dimen name="balance_seekbar_center_marker_height">14dp</dimen>
<dimen name="balance_seekbar_center_marker_width">1dp</dimen>
<dimen name="crypt_clock_size">100sp</dimen>
<dimen name="divider_height">3dip</dimen>

View File

@@ -33,8 +33,4 @@
<item>@string/input_method_selector_always_show_value</item>
<item>@string/input_method_selector_always_hide_value</item>
</string-array>
<string name="category_key_wireless">com.android.settings.category.wireless</string>
<string name="category_key_device">com.android.settings.category.device</string>
<string name="category_key_personal">com.android.settings.category.personal</string>
<string name="category_key_system">com.android.settings.category.system</string>
</resources>

View File

@@ -4487,8 +4487,8 @@
<string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
<!-- Summary text for the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=100] -->
<string name="keyboard_shortcuts_helper_summary">Display available shortcuts</string>
<!-- Title for the 'Work profile' preference category inside Languages and inputs'. [CHAR LIMIT=45] -->
<string name="language_and_input_for_work_category_title">Work profile</string>
<!-- Title for the 'Work profile input & assistance' preference category inside Languages and inputs'. [CHAR LIMIT=50] -->
<string name="language_and_input_for_work_category_title">Work profile input &amp; assistance</string>
<!-- Title for the 'Virtual keyboards for work' preference. [CHAR LIMIT=45] -->
<string name="virtual_keyboards_for_work_title">Virtual keyboard for work</string>
@@ -4717,6 +4717,12 @@
<string name="accessibility_toggle_master_mono_title">Mono audio</string>
<!-- Summary for the accessibility preference for master mono. [CHAR LIMIT=50] -->
<string name="accessibility_toggle_master_mono_summary">Combine channels when playing audio</string>
<!-- Title for the accessibility preference for master balance. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_master_balance_title">Audio balance</string>
<!-- 'Left' balance text for the accessibility preference for master balance. [CHAR LIMIT=20] -->
<string name="accessibility_toggle_master_balance_left_label">Left</string>
<!-- 'Right' balance text for the accessibility preference for master balance. [CHAR LIMIT=20] -->
<string name="accessibility_toggle_master_balance_right_label">Right</string>
<!-- Option heading to leave the timeout requirement for accessibility users at its default level. [CHAR LIMIT=35] -->
<string name="accessibility_timeout_default">Default</string>
@@ -5851,9 +5857,9 @@
<!-- Summary for whether to enable SMS access restriction [CHAR LIMIT=NONE]-->
<string name="sms_access_restriction_enabled_summary">Only default phone and messaging apps have SMS &amp; call log permissions</string>
<!-- Title for the new device identifier access restrictions [CHAR LIMIT=50]-->
<string name="device_identifier_access_restrictions_title">Enable device identifier restrictions</string>
<string name="device_identifier_access_restrictions_title">Disable device identifier restrictions</string>
<!-- Summary for the new device identifier access restrictions [CHAR LIMIT=NONE]-->
<string name="device_identifier_access_restrictions_summary">Enable the new access restrictions for device identifiers.</string>
<string name="device_identifier_access_restrictions_summary">Disable the new access restrictions for device identifiers</string>
<!-- Message when there are no available trust agents to display -->
@@ -7621,6 +7627,9 @@
<!-- Configure Notifications: Title for the notification badging option. [CHAR LIMIT=30 BACKUP_MESSAGE_ID=5125022693565388760] -->
<string name="notification_badging_title">Allow notification dots</string>
<!-- Configure Notifications: Title for the notification bubbles option. [CHAR LIMIT=60] -->
<string name="notification_bubbles_title">Allow notification bubbles</string>
<!-- Configure Notifications: Title for the pulse notification light option. [CHAR LIMIT=30] -->
<string name="notification_pulse_title">Blink light</string>
@@ -10470,6 +10479,12 @@
<!-- Label on the confirmation button of a dialog that lets a user set the display name of a
mobile network subscription [CHAR LIMIT=20] -->
<string name="mobile_network_sim_name_rename">Rename</string>
<!-- Label for the on position of a switch on the mobile network details page which allows
disabling/enabling a SIM. The SIM is enabled in this state. [CHAR LIMIT=40] -->
<string name="mobile_network_use_sim_on">Use SIM</string>
<!-- Label for the off position of a switch on the mobile network details page which allows
disabling/enabling a SIM. The SIM is disabled in this state. [CHAR LIMIT=40] -->
<string name="mobile_network_use_sim_off">Off</string>
<!-- Title for preferred network type [CHAR LIMIT=NONE] -->
<string name="preferred_network_mode_title">Preferred network type</string>

View File

@@ -419,7 +419,7 @@
<item name="android:layout_marginBottom">@dimen/homepage_card_vertical_margin</item>
<item name="android:layout_marginStart">@dimen/homepage_card_side_margin</item>
<item name="android:layout_marginEnd">@dimen/homepage_card_side_margin</item>
<item name="cardCornerRadius">8dp</item>
<item name="cardCornerRadius">@*android:dimen/config_dialogCornerRadius</item>
<item name="cardElevation">0dp</item>
<item name="strokeColor">@color/homepage_card_stroke_color</item>
<item name="strokeWidth">1dp</item>

View File

@@ -130,6 +130,10 @@
android:summary="@string/accessibility_toggle_master_mono_summary"
android:persistent="false"/>
<com.android.settings.accessibility.BalanceSeekBarPreference
android:key="seekbar_master_balance"
android:title="@string/accessibility_toggle_master_balance_title" />
<Preference
android:key="hearing_aid_preference"
android:summary="@string/accessibility_hearingaid_not_connected_summary"

View File

@@ -60,9 +60,14 @@
settings:useAdditionalSummary="true"
android:order="1001"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<com.android.settingslib.RestrictedSwitchPreference
android:key="bubble"
android:title="@string/notification_bubbles_title"
android:order="1002"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<Preference
android:key="app_link"
android:order="1002"
android:order="1003"
android:title="@string/app_settings_link" />
</PreferenceCategory>

View File

@@ -86,17 +86,23 @@
settings:useAdditionalSummary="true"
settings:restrictedSwitchSummary="@string/enabled_by_admin"/>
<com.android.settingslib.RestrictedSwitchPreference
android:key="bubble"
android:title="@string/notification_bubbles_title"
android:order="16"
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<!-- Bypass DND -->
<com.android.settingslib.RestrictedSwitchPreference
android:key="bypass_dnd"
android:order="16"
android:order="17"
android:title="@string/app_notification_override_dnd_title"
android:summary="@string/app_notification_override_dnd_summary"
settings:useAdditionalSummary="true"/>
<Preference
android:key="app_link"
android:order="17"
android:order="18"
android:title="@string/app_settings_link"
settings:allowDividerAbove="true"/>
</PreferenceCategory>

View File

@@ -38,6 +38,12 @@
android:title="@string/notification_badging_title"
settings:controller="com.android.settings.notification.BadgingNotificationPreferenceController"/>
<!-- Notification bubbles -->
<SwitchPreference
android:key="notification_bubbles"
android:title="@string/notification_bubbles_title"
settings:controller="com.android.settings.notification.BubbleNotificationPreferenceController"/>
<!-- Pulse notification light -->
<SwitchPreference
android:key="notification_pulse"

View File

@@ -17,7 +17,12 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="mobile_network_pref_screen"
settings:initialExpandedChildrenCount="7">
settings:initialExpandedChildrenCount="8">
<com.android.settingslib.widget.LayoutPreference
android:key="use_sim_switch"
android:layout="@layout/styled_switch_bar"
settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/>
<com.android.settings.datausage.DataUsageSummaryPreference
android:key="status_header"

View File

@@ -71,6 +71,7 @@ public class Settings extends SettingsActivity {
public static class AppUsageAccessSettingsActivity extends SettingsActivity { /* empty */ }
public static class LocationSettingsActivity extends SettingsActivity { /* empty */ }
public static class ScanningSettingsActivity extends SettingsActivity { /* empty */ }
public static class PrivacyDashboardActivity extends SettingsActivity { /* empty */ }
public static class PrivacySettingsActivity extends SettingsActivity { /* empty */ }
public static class FactoryResetActivity extends SettingsActivity { /* empty */ }
public static class RunningServicesActivity extends SettingsActivity { /* empty */ }

View File

@@ -0,0 +1,152 @@
/*
* 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.accessibility;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.widget.SeekBar;
import com.android.settings.R;
/**
* A custom seekbar for the balance setting.
*
* Adds a center line indicator between left and right, which snaps to if close.
* Updates Settings.System for balance on progress changed.
*/
public class BalanceSeekBar extends SeekBar {
private static final String TAG = "BalanceSeekBar";
private final Context mContext;
private final Object mListenerLock = new Object();
private OnSeekBarChangeListener mOnSeekBarChangeListener;
private final OnSeekBarChangeListener mProxySeekBarListener = new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
synchronized(mListenerLock) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStopTrackingTouch(seekBar);
}
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
synchronized(mListenerLock) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onStartTrackingTouch(seekBar);
}
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
// Snap to centre when within the specified threshold
if (progress != mCenter
&& progress > mCenter - mSnapThreshold
&& progress < mCenter + mSnapThreshold) {
progress = mCenter;
seekBar.setProgress(progress); // direct update (fromUser becomes false)
}
final float balance = (progress - mCenter) * 0.01f;
Settings.System.putFloatForUser(mContext.getContentResolver(),
Settings.System.MASTER_BALANCE, balance, UserHandle.USER_CURRENT);
}
// If fromUser is false, the call is a set from the framework on creation or on
// internal update. The progress may be zero, ignore (don't change system settings).
// after adjusting the seekbar, notify downstream listener.
// note that progress may have been adjusted in the code above to mCenter.
synchronized(mListenerLock) {
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(seekBar, progress, fromUser);
}
}
}
};
// Percentage of max to be used as a snap to threshold
private static final float SNAP_TO_PERCENTAGE = 0.03f;
private final Paint mCenterMarkerPaint;
private final Rect mCenterMarkerRect;
// changed in setMax()
private float mSnapThreshold;
private int mCenter;
public BalanceSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.seekBarStyle);
}
public BalanceSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0 /* defStyleRes */);
}
public BalanceSeekBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mContext = context;
Resources res = getResources();
mCenterMarkerRect = new Rect(0 /* left */, 0 /* top */,
res.getDimensionPixelSize(R.dimen.balance_seekbar_center_marker_width),
res.getDimensionPixelSize(R.dimen.balance_seekbar_center_marker_height));
mCenterMarkerPaint = new Paint();
// TODO use a more suitable colour?
mCenterMarkerPaint.setColor(Color.BLACK);
mCenterMarkerPaint.setStyle(Paint.Style.FILL);
// Remove the progress colour
setProgressTintList(ColorStateList.valueOf(Color.TRANSPARENT));
super.setOnSeekBarChangeListener(mProxySeekBarListener);
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener listener) {
synchronized(mListenerLock) {
mOnSeekBarChangeListener = listener;
}
}
// Note: the superclass AbsSeekBar.setMax is synchronized.
@Override
public synchronized void setMax(int max) {
super.setMax(max);
// update snap to threshold
mCenter = max / 2;
mSnapThreshold = max * SNAP_TO_PERCENTAGE;
}
// Note: the superclass AbsSeekBar.onDraw is synchronized.
@Override
protected synchronized void onDraw(Canvas canvas) {
// Draw a vertical line at 50% that represents centred balance
int seekBarCenter = (canvas.getHeight() - getPaddingBottom()) / 2;
canvas.save();
canvas.translate((canvas.getWidth() - mCenterMarkerRect.right) / 2,
seekBarCenter - (mCenterMarkerRect.bottom / 2));
canvas.drawRect(mCenterMarkerRect, mCenterMarkerPaint);
canvas.restore();
super.onDraw(canvas);
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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.accessibility;
import android.content.Context;
import android.media.AudioSystem;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.SeekBar;
import androidx.core.content.res.TypedArrayUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.widget.SeekBarPreference;
/** A slider preference that directly controls audio balance **/
public class BalanceSeekBarPreference extends SeekBarPreference {
private static final String TAG = "BalanceSeekBarPreference";
private final Context mContext;
private BalanceSeekBar mSeekBar;
private ImageView mIconView;
public BalanceSeekBarPreference(Context context, AttributeSet attrs) {
super(context, attrs, TypedArrayUtils.getAttr(context,
R.attr.preferenceStyle,
android.R.attr.preferenceStyle));
mContext = context;
setLayoutResource(R.layout.preference_balance_slider);
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
super.onBindViewHolder(view);
mSeekBar = (BalanceSeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
init();
}
private void init() {
if (mSeekBar == null) {
return;
}
final float balance = Settings.System.getFloatForUser(
mContext.getContentResolver(), Settings.System.MASTER_BALANCE,
0.f /* default */, UserHandle.USER_CURRENT);
// Rescale balance to range 0-200 centered at 100.
mSeekBar.setMax(200);
mSeekBar.setProgress((int)(balance * 100.f) + 100);
mSeekBar.setEnabled(isEnabled());
}
}

View File

@@ -274,6 +274,7 @@ public class SettingsGateway {
Settings.StorageDashboardActivity.class.getName(),
Settings.PowerUsageSummaryActivity.class.getName(),
Settings.AccountDashboardActivity.class.getName(),
Settings.PrivacySettingsActivity.class.getName(),
Settings.SecurityDashboardActivity.class.getName(),
Settings.AccessibilitySettingsActivity.class.getName(),
Settings.SystemDashboardActivity.class.getName(),

View File

@@ -50,12 +50,12 @@ public class DeviceIdentifierAccessRestrictionsPreferenceController
private void writeSetting(boolean isEnabled) {
DeviceConfig.setProperty(DeviceConfig.Privacy.NAMESPACE,
DeviceConfig.Privacy.PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED,
String.valueOf(!isEnabled), false);
String.valueOf(isEnabled), false);
}
@Override
public void updateState(Preference preference) {
boolean isEnabled = !Boolean.parseBoolean(
boolean isEnabled = Boolean.parseBoolean(
DeviceConfig.getProperty(DeviceConfig.Privacy.NAMESPACE,
DeviceConfig.Privacy.PROPERTY_DEVICE_IDENTIFIER_ACCESS_RESTRICTIONS_DISABLED));
((SwitchPreference) mPreference).setChecked(isEnabled);
@@ -64,7 +64,7 @@ public class DeviceIdentifierAccessRestrictionsPreferenceController
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
writeSetting(true);
writeSetting(false);
((SwitchPreference) mPreference).setChecked(true);
}
}

View File

@@ -132,6 +132,7 @@ public class MobileNetworkSettings extends RestrictedDashboardFragment {
if (FeatureFlagPersistent.isEnabled(getContext(), FeatureFlags.NETWORK_INTERNET_V2)) {
use(CallsDefaultSubscriptionController.class).init(getLifecycle());
use(SmsDefaultSubscriptionController.class).init(getLifecycle());
use(MobileNetworkSwitchController.class).init(getLifecycle(), mSubId);
}
use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId);

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network.telephony;
import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE;
import static androidx.lifecycle.Lifecycle.Event.ON_RESUME;
import android.content.Context;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.FeatureFlags;
import com.android.settings.development.featureflags.FeatureFlagPersistent;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.SubscriptionsChangeListener;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.PreferenceScreen;
/** This controls a switch to allow enabling/disabling a mobile network */
public class MobileNetworkSwitchController extends BasePreferenceController implements
SubscriptionsChangeListener.SubscriptionsChangeListenerClient, LifecycleObserver {
private static final String TAG = "MobileNetworkSwitchCtrl";
private SwitchBar mSwitchBar;
private int mSubId;
private SubscriptionsChangeListener mChangeListener;
private SubscriptionManager mSubscriptionManager;
public MobileNetworkSwitchController(Context context, String preferenceKey) {
super(context, preferenceKey);
mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
mChangeListener = new SubscriptionsChangeListener(context, this);
}
public void init(Lifecycle lifecycle, int subId) {
lifecycle.addObserver(this);
mSubId = subId;
}
@OnLifecycleEvent(ON_RESUME)
public void onResume() {
mChangeListener.start();
update();
}
@OnLifecycleEvent(ON_PAUSE)
public void onPause() {
mChangeListener.stop();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference pref = screen.findPreference(mPreferenceKey);
mSwitchBar = pref.findViewById(R.id.switch_bar);
mSwitchBar.setSwitchBarText(R.string.mobile_network_use_sim_on,
R.string.mobile_network_use_sim_off);
mSwitchBar.addOnSwitchChangeListener((switchView, isChecked) -> {
if (mSubscriptionManager.isSubscriptionEnabled(mSubId) != isChecked) {
mSubscriptionManager.setSubscriptionEnabled(mSubId, isChecked);
}
});
update();
}
private void update() {
if (mSwitchBar == null) {
return;
}
final List<SubscriptionInfo> subs = SubscriptionUtil.getAvailableSubscriptions(
mSubscriptionManager);
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID || subs.size() < 2) {
mSwitchBar.hide();
return;
}
for (SubscriptionInfo info : subs) {
if (info.getSubscriptionId() == mSubId) {
mSwitchBar.show();
mSwitchBar.setChecked(mSubscriptionManager.isSubscriptionEnabled(mSubId));
return;
}
}
// This subscription was not found in the available list.
mSwitchBar.hide();
}
@Override
public int getAvailabilityStatus() {
if (FeatureFlagPersistent.isEnabled(mContext, FeatureFlags.NETWORK_INTERNET_V2)) {
return AVAILABLE_UNSEARCHABLE;
} else {
return CONDITIONALLY_UNAVAILABLE;
}
}
@Override
public void onAirplaneModeChanged(boolean airplaneModeEnabled) {}
@Override
public void onSubscriptionsChanged() {
update();
}
}

View File

@@ -152,6 +152,7 @@ public class AppNotificationSettings extends NotificationSettingsBase {
mControllers.add(new DescriptionPreferenceController(context));
mControllers.add(new NotificationsOffPreferenceController(context));
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
mControllers.add(new BubblePreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}

View File

@@ -60,7 +60,7 @@ public class BadgePreferenceController extends NotificationPreferenceController
if (isDefaultChannel()) {
return true;
} else {
return mAppRow.showBadge;
return mAppRow == null ? false : mAppRow.showBadge;
}
}
return true;

View File

@@ -0,0 +1,124 @@
/*
* 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.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
import android.text.TextUtils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
public class BubbleNotificationPreferenceController extends TogglePreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
LifecycleObserver, OnResume, OnPause {
private static final String TAG = "BubbleNotifPrefContr";
@VisibleForTesting
static final int ON = 1;
@VisibleForTesting
static final int OFF = 0;
private SettingObserver mSettingObserver;
public BubbleNotificationPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
Preference preference = screen.findPreference(NOTIFICATION_BUBBLES);
if (preference != null) {
mSettingObserver = new SettingObserver(preference);
}
}
@Override
public void onResume() {
if (mSettingObserver != null) {
mSettingObserver.register(mContext.getContentResolver(), true /* register */);
}
}
@Override
public void onPause() {
if (mSettingObserver != null) {
mSettingObserver.register(mContext.getContentResolver(), false /* register */);
}
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public boolean isChecked() {
return Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, ON) == ON;
}
@Override
public boolean setChecked(boolean isChecked) {
return Settings.Secure.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, isChecked ? ON : OFF);
}
class SettingObserver extends ContentObserver {
private final Uri NOTIFICATION_BUBBLES_URI =
Settings.Secure.getUriFor(NOTIFICATION_BUBBLES);
private final Preference mPreference;
public SettingObserver(Preference preference) {
super(new Handler());
mPreference = preference;
}
public void register(ContentResolver cr, boolean register) {
if (register) {
cr.registerContentObserver(NOTIFICATION_BUBBLES_URI, false, this);
} else {
cr.unregisterContentObserver(this);
}
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (NOTIFICATION_BUBBLES_URI.equals(uri)) {
updateState(mPreference);
}
}
}
}

View File

@@ -0,0 +1,94 @@
/*
* 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.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import android.content.Context;
import android.provider.Settings;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.RestrictedSwitchPreference;
import androidx.preference.Preference;
public class BubblePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
private static final String TAG = "BubblePrefContr";
private static final String KEY = "bubble";
private static final int SYSTEM_WIDE_ON = 1;
private static final int SYSTEM_WIDE_OFF = 0;
public BubblePreferenceController(Context context, NotificationBackend backend) {
super(context, backend);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
return false;
}
if (mAppRow == null && mChannel == null) {
return false;
}
if (Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON) == SYSTEM_WIDE_OFF) {
return false;
}
if (mChannel != null) {
if (isDefaultChannel()) {
return true;
} else {
return mAppRow == null ? false : mAppRow.allowBubbles;
}
}
return true;
}
public void updateState(Preference preference) {
if (mAppRow != null) {
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
pref.setDisabledByAdmin(mAdmin);
if (mChannel != null) {
pref.setChecked(mChannel.canBubble());
pref.setEnabled(isChannelConfigurable() && !pref.isDisabledByAdmin());
} else {
pref.setChecked(mAppRow.allowBubbles);
}
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean value = (Boolean) newValue;
if (mChannel != null) {
mChannel.setAllowBubbles(value);
saveChannel();
} else if (mAppRow != null){
mAppRow.allowBubbles = value;
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value);
}
return true;
}
}

View File

@@ -110,6 +110,7 @@ public class ChannelNotificationSettings extends NotificationSettingsBase {
mControllers.add(new BadgePreferenceController(context, mBackend));
mControllers.add(new DndPreferenceController(context, mBackend));
mControllers.add(new NotificationsOffPreferenceController(context));
mControllers.add(new BubblePreferenceController(context, mBackend));
return new ArrayList<>(mControllers);
}
}

View File

@@ -71,6 +71,7 @@ public class NotificationBackend {
row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app);
row.banned = getNotificationsBanned(row.pkg, row.uid);
row.showBadge = canShowBadge(row.pkg, row.uid);
row.allowBubbles = canBubble(row.pkg, row.uid);
row.userId = UserHandle.getUserId(row.uid);
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
row.channelCount = getChannelCount(row.pkg, row.uid);
@@ -175,6 +176,26 @@ public class NotificationBackend {
}
}
public boolean canBubble(String pkg, int uid) {
try {
return sINM.areBubblesAllowedForPackage(pkg, uid);
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return false;
}
}
public boolean setAllowBubbles(String pkg, int uid, boolean allow) {
try {
sINM.setBubblesAllowed(pkg, uid, allow);
return true;
} catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e);
return false;
}
}
public NotificationChannel getChannel(String pkg, int uid, String channelId) {
if (channelId == null) {
return null;
@@ -416,6 +437,7 @@ public class NotificationBackend {
public boolean lockedImportance;
public String lockedChannelId;
public boolean showBadge;
public boolean allowBubbles;
public int userId;
public int blockedChannelCount;
public int channelCount;

View File

@@ -157,7 +157,6 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
boolean frp = KeyguardManager.ACTION_CONFIRM_FRP_CREDENTIAL.equals(intent.getAction());
mUserId = UserHandle.myUserId();
final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
if (isInternalActivity()) {
try {
mUserId = Utils.getUserIdFromBundle(this, intent.getExtras());
@@ -165,6 +164,7 @@ public class ConfirmDeviceCredentialActivity extends FragmentActivity {
Log.e(TAG, "Invalid intent extra", se);
}
}
final int effectiveUserId = mUserManager.getCredentialOwnerProfile(mUserId);
final boolean isManagedProfile = UserManager.get(this).isManagedProfile(mUserId);
// if the client app did not hand in a title and we are about to show the work challenge,
// check whether there is a policy setting the organization name and use that as title

View File

@@ -31,16 +31,13 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.settings.R;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -57,8 +54,6 @@ import org.robolectric.shadows.ShadowUserManager;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = BackupSettingsHelperTest.ShadowBackupManagerStub.class)
public class BackupSettingsHelperTest {
private static final String DEFAULT_SETTINGS_CLASSNAME =
"com.android.settings.Settings$PrivacySettingsActivity";
private static final int DEFAULT_SUMMARY_RESOURCE =
R.string.backup_configure_account_default_summary;
@@ -294,16 +289,6 @@ public class BackupSettingsHelperTest {
assertThat(backupIntent).isEqualTo(intent);
}
@Test
public void testGetIntentForBackupSettings_WithoutIntentFromTransport() throws Exception {
when(mBackupManager.getDataManagementIntent(anyString())).thenReturn(null);
Intent backupIntent = mBackupSettingsHelper.getIntentForBackupSettings();
assertThat(backupIntent.getComponent().getClassName())
.isEqualTo(DEFAULT_SETTINGS_CLASSNAME);
}
@Test
public void testGetLabelForBackupSettings_WithLabelFromTransport() throws Exception {
String label = "test_label";

View File

@@ -0,0 +1,140 @@
/*
* 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.network.telephony;
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.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import com.android.settings.R;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.widget.SwitchBar;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
@RunWith(RobolectricTestRunner.class)
public class MobileNetworkSwitchControllerTest {
@Mock
private SubscriptionManager mSubscriptionManager;
@Mock
private PreferenceScreen mScreen;
@Mock
private LayoutPreference mLayoutPreference;
@Mock
private SubscriptionInfo mSubscription;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private MobileNetworkSwitchController mController;
private SwitchBar mSwitchBar;
private int mSubId = 123;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
when(mSubscription.getSubscriptionId()).thenReturn(mSubId);
// Most tests want to have 2 available subscriptions so that the switch bar will show.
SubscriptionInfo sub2 = mock(SubscriptionInfo.class);
when(sub2.getSubscriptionId()).thenReturn(456);
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription, sub2));
String key = "prefKey";
mController = new MobileNetworkSwitchController(mContext, key);
mController.init(mLifecycle, mSubscription.getSubscriptionId());
mSwitchBar = new SwitchBar(mContext);
when(mScreen.findPreference(key)).thenReturn(mLayoutPreference);
when(mLayoutPreference.findViewById(R.id.switch_bar)).thenReturn(mSwitchBar);
}
@After
public void cleanUp() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
}
@Test
public void displayPreference_onlyOneSubscription_switchBarHidden() {
SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscription));
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isFalse();
}
@Test
public void displayPreference_subscriptionEnabled_switchIsOn() {
when(mSubscriptionManager.isSubscriptionEnabled(mSubId)).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isTrue();
}
@Test
public void displayPreference_subscriptionDisabled_switchIsOff() {
when(mSubscriptionManager.isSubscriptionEnabled(mSubId)).thenReturn(false);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isFalse();
}
@Test
public void switchChangeListener_fromEnabledToDisabled_setSubscriptionEnabledCalledCorrectly() {
when(mSubscriptionManager.isSubscriptionEnabled(mSubId)).thenReturn(true);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isTrue();
mSwitchBar.setChecked(false);
verify(mSubscriptionManager).setSubscriptionEnabled(eq(mSubId), eq(false));
}
@Test
public void switchChangeListener_fromDisabledToEnabled_setSubscriptionEnabledCalledCorrectly() {
when(mSubscriptionManager.isSubscriptionEnabled(mSubId)).thenReturn(false);
mController.displayPreference(mScreen);
assertThat(mSwitchBar.isShowing()).isTrue();
assertThat(mSwitchBar.isChecked()).isFalse();
mSwitchBar.setChecked(true);
verify(mSubscriptionManager).setSubscriptionEnabled(eq(mSubId), eq(true));
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static com.android.settings.notification.BadgingNotificationPreferenceController.OFF;
import static com.android.settings.notification.BadgingNotificationPreferenceController.ON;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.provider.Settings;
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 androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
@RunWith(RobolectricTestRunner.class)
public class BubbleNotificationPreferenceControllerTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private BubbleNotificationPreferenceController mController;
private Preference mPreference;
private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new BubbleNotificationPreferenceController(mContext,
KEY_NOTIFICATION_BUBBLES);
mPreference = new Preference(RuntimeEnvironment.application);
mPreference.setKey(mController.getPreferenceKey());
when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference);
}
@Test
public void display_shouldDisplay() {
assertThat(mPreference.isVisible()).isTrue();
}
@Test
public void updateState_preferenceSetCheckedWhenSettingIsOn() {
final TwoStatePreference preference = mock(TwoStatePreference.class);
final Context context = RuntimeEnvironment.application;
Settings.Secure.putInt(context.getContentResolver(), NOTIFICATION_BUBBLES, ON);
mController.updateState(preference);
verify(preference).setChecked(true);
}
@Test
public void updateState_preferenceSetUncheckedWhenSettingIsOff() {
final TwoStatePreference preference = mock(TwoStatePreference.class);
final Context context = RuntimeEnvironment.application;
Settings.Secure.putInt(context.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
mController.updateState(preference);
verify(preference).setChecked(false);
}
@Test
public void isChecked_settingIsOff_shouldReturnFalse() {
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
assertThat(mController.isChecked()).isFalse();
}
@Test
public void isChecked_settingIsOn_shouldReturnTrue() {
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
assertThat(mController.isChecked()).isTrue();
}
@Test
public void setChecked_setFalse_disablesSetting() {
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
mController.setChecked(false);
int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, -1);
assertThat(updatedValue).isEqualTo(OFF);
}
@Test
public void setChecked_setTrue_enablesSetting() {
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
mController.setChecked(true);
int updatedValue = Settings.Secure.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, -1);
assertThat(updatedValue).isEqualTo(ON);
}
@Test
public void isSliceable_returnsFalse() {
assertThat(mController.isSliceable()).isFalse();
}
}

View File

@@ -0,0 +1,311 @@
/*
* 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.NotificationChannel.DEFAULT_CHANNEL_ID;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE;
import static android.provider.Settings.Secure.NOTIFICATION_BUBBLES;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
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 android.provider.Settings;
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 BubblePreferenceControllerTest {
private Context mContext;
@Mock
private NotificationBackend mBackend;
@Mock
private NotificationManager mNm;
@Mock
private UserManager mUm;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen;
private BubblePreferenceController 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 BubblePreferenceController(mContext, mBackend));
}
@Test
public void testNoCrashIfNoOnResume() {
mController.isAvailable();
mController.updateState(mock(RestrictedSwitchPreference.class));
mController.onPreferenceChange(mock(RestrictedSwitchPreference.class), true);
}
@Test
public void testIsAvailable_notIfAppBlocked() {
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_channel_notIfAppOff() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_notIfOffGlobally() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 0);
assertFalse(mController.isAvailable());
}
@Test
public void testIsAvailable_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null);
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
assertTrue(mController.isAvailable());
}
@Test
public void testIsAvailable_defaultChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
mController.onResume(appRow, channel, null, null);
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
assertTrue(mController.isAvailable());
}
@Test
public void testIsAvailable_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
assertTrue(mController.isAvailable());
}
@Test
public void testIsAvailable_channelAppOff() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null);
Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, 1);
assertFalse(mController.isAvailable());
}
@Test
public void testUpdateState_disabledByAdmin() {
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn("something");
mController.onResume(new NotificationBackend.AppRow(), channel, null,
mock(RestrictedLockUtils.EnforcedAdmin.class));
Preference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_channelNotConfigurable() {
String lockedId = "locked";
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.lockedChannelId = lockedId;
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getId()).thenReturn(lockedId);
mController.onResume(appRow, channel, null, null);
Preference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertFalse(pref.isEnabled());
}
@Test
public void testUpdateState_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.lockedChannelId = "a";
NotificationChannel channel = mock(NotificationChannel.class);
when(channel.canBubble()).thenReturn(true);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertTrue(pref.isChecked());
when(channel.canBubble()).thenReturn(false);
mController.onResume(appRow, channel, null, null);
mController.updateState(pref);
assertFalse(pref.isChecked());
}
@Test
public void testUpdateState_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
mController.onResume(appRow, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
mController.updateState(pref);
assertTrue(pref.isChecked());
appRow.allowBubbles = false;
mController.onResume(appRow, null, null, null);
mController.updateState(pref);
assertFalse(pref.isChecked());
}
@Test
public void testOnPreferenceChange_on_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
channel.setAllowBubbles(false);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, true);
assertTrue(channel.canBubble());
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
}
@Test
public void testOnPreferenceChange_off_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
channel.setAllowBubbles(true);
mController.onResume(appRow, channel, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, false);
verify(mBackend, times(1)).updateChannel(any(), anyInt(), any());
assertFalse(channel.canBubble());
}
@Test
public void testOnPreferenceChange_on_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false;
mController.onResume(appRow, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, true);
assertTrue(appRow.allowBubbles);
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(true));
}
@Test
public void testOnPreferenceChange_off_app() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
mController.onResume(appRow, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen);
mController.updateState(pref);
mController.onPreferenceChange(pref, false);
assertFalse(appRow.allowBubbles);
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(false));
}
}