Bubble settings: feature, notification, app
* Removed setting from developer options * Removed bubble settings from normal notifications / channels * Feature available via notification setting * Feature screen with educational gif * App level is now a tri-state choice of all / selected / none * App level bubble controls are accessible top-level in app notifications Test: make -j40 RunSettingsRoboTests ROBOTEST_FILTER="Bubble" Bug: 138116133 Change-Id: Id103e9d3717fdc9b86a916be40c43cda9c35ac34
This commit is contained in:
@@ -20,6 +20,6 @@
|
||||
android:color="@color/notification_importance_selection_bg" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/notification_importance_button_selected"/>
|
||||
android:color="?android:attr/colorAccent"/>
|
||||
<corners android:radius="@dimen/rect_button_radius" />
|
||||
</shape>
|
||||
|
25
res/drawable/ic_bubble_all.xml
Normal file
25
res/drawable/ic_bubble_all.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
25
res/drawable/ic_bubble_none.xml
Normal file
25
res/drawable/ic_bubble_none.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10 10,-4.5 10,-10S17.5,2 12,2zM4,12c0,-4.4 3.6,-8 8,-8 1.8,0 3.5,0.6 4.9,1.7L5.7,16.9C4.6,15.5 4,13.8 4,12zM12,20c-1.8,0 -3.5,-0.6 -4.9,-1.7L18.3,7.1C19.4,8.5 20,10.2 20,12c0,4.4 -3.6,8 -8,8z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
25
res/drawable/ic_bubble_selected.xml
Normal file
25
res/drawable/ic_bubble_selected.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:pathData="M15,8c0,-1.42 -0.5,-2.73 -1.33,-3.76 0.42,-0.14 0.86,-0.24 1.33,-0.24 2.21,0 4,1.79 4,4s-1.79,4 -4,4c-0.43,0 -0.84,-0.09 -1.23,-0.21 -0.03,-0.01 -0.06,-0.02 -0.1,-0.03C14.5,10.73 15,9.42 15,8zM16.66,13.13C18.03,14.06 19,15.32 19,17v3h4v-3c0,-2.18 -3.58,-3.47 -6.34,-3.87zM9,6c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2m0,9c-2.7,0 -5.8,1.29 -6,2.01L3,18h12v-1c-0.2,-0.71 -3.3,-2 -6,-2M9,4c2.21,0 4,1.79 4,4s-1.79,4 -4,4 -4,-1.79 -4,-4 1.79,-4 4,-4zM9,13c2.67,0 8,1.34 8,4v3L1,20v-3c0,-2.66 5.33,-4 8,-4z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
@@ -14,17 +14,13 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9c0,1.39 0.32,2.69 0.88,3.86l1.53,-1.53C5.15,13.6 5,12.82 5,12c0,-3.86 3.14,-7 7,-7s7,3.14 7,7s-3.14,7 -7,7c-0.83,0 -1.62,-0.15 -2.35,-0.42l-1.53,1.53C9.3,20.67 10.61,21 12,21c4.97,0 9,-4.03 9,-9C21,7.03 16.97,3 12,3z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M12.99,15.99l2,0l0,-7l-7,0l0,2l3.59,0l-8.79,8.8l1.41,1.41l8.79,-8.79z"
|
||||
android:fillColor="#000000"/>
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M22,12C22,12 22,12 22,12C22,12 22,12 22,12c0,0.56 -0.06,1.1 -0.15,1.64l-1.97,-0.33c0.15,-0.91 0.15,-1.84 -0.02,-2.75c-0.01,-0.03 -0.01,-0.07 -0.02,-0.1c-0.03,-0.18 -0.08,-0.36 -0.13,-0.54c-0.02,-0.08 -0.04,-0.16 -0.06,-0.24c-0.04,-0.14 -0.09,-0.27 -0.14,-0.41c-0.04,-0.12 -0.08,-0.24 -0.13,-0.35c-0.04,-0.09 -0.08,-0.18 -0.13,-0.27c-0.07,-0.15 -0.14,-0.3 -0.22,-0.45c-0.03,-0.05 -0.06,-0.09 -0.08,-0.14c-0.72,-1.26 -1.77,-2.31 -3.03,-3.03c-0.05,-0.03 -0.09,-0.06 -0.14,-0.08c-0.15,-0.08 -0.3,-0.15 -0.45,-0.22c-0.09,-0.04 -0.18,-0.09 -0.27,-0.13c-0.11,-0.05 -0.23,-0.09 -0.35,-0.13c-0.14,-0.05 -0.27,-0.1 -0.41,-0.14c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.18,-0.05 -0.36,-0.1 -0.54,-0.13c-0.03,-0.01 -0.07,-0.01 -0.1,-0.01c-0.95,-0.17 -1.93,-0.17 -2.88,0c-0.03,0.01 -0.07,0.01 -0.1,0.01c-0.18,0.04 -0.36,0.08 -0.54,0.13C9.85,4.3 9.77,4.32 9.69,4.34C9.55,4.38 9.42,4.44 9.28,4.49C9.17,4.53 9.05,4.57 8.93,4.61C8.84,4.65 8.75,4.7 8.66,4.74c-0.15,0.07 -0.3,0.14 -0.45,0.22C8.16,4.98 8.12,5.01 8.07,5.04C5.64,6.42 4,9.02 4,12c0,2.74 1.39,5.16 3.49,6.6c0.01,0.01 0.03,0.02 0.04,0.03c0.16,0.11 0.33,0.2 0.49,0.3c0.06,0.04 0.12,0.08 0.19,0.11c0.13,0.07 0.27,0.13 0.4,0.19c0.11,0.05 0.21,0.1 0.32,0.15c0.1,0.04 0.2,0.07 0.29,0.11c0.15,0.06 0.31,0.11 0.46,0.16c0.05,0.02 0.11,0.03 0.17,0.04c1.11,0.31 2.27,0.35 3.4,0.18l0.35,1.98c-0.54,0.09 -1.08,0.14 -1.62,0.14V22c-0.65,0 -1.28,-0.07 -1.9,-0.19c-0.01,0 -0.01,0 -0.02,0c-0.25,-0.05 -0.49,-0.11 -0.73,-0.18c-0.08,-0.02 -0.16,-0.04 -0.23,-0.06c-0.19,-0.06 -0.37,-0.13 -0.55,-0.19c-0.13,-0.05 -0.26,-0.09 -0.39,-0.14c-0.13,-0.05 -0.25,-0.12 -0.38,-0.18c-0.18,-0.08 -0.35,-0.16 -0.53,-0.25c-0.07,-0.04 -0.14,-0.08 -0.21,-0.13c-0.22,-0.12 -0.43,-0.25 -0.64,-0.39c-0.01,-0.01 -0.02,-0.02 -0.04,-0.03c-0.51,-0.35 -1,-0.74 -1.45,-1.2l0,0C3.12,17.26 2,14.76 2,12c0,-2.76 1.12,-5.26 2.93,-7.07l0,0c0.45,-0.45 0.93,-0.84 1.44,-1.19C6.39,3.73 6.4,3.72 6.42,3.71c0.2,-0.14 0.41,-0.26 0.62,-0.38c0.08,-0.05 0.15,-0.09 0.23,-0.14c0.17,-0.09 0.33,-0.16 0.5,-0.24c0.13,-0.06 0.27,-0.13 0.4,-0.19C8.3,2.71 8.42,2.67 8.55,2.63c0.19,-0.07 0.38,-0.14 0.58,-0.2c0.07,-0.02 0.14,-0.03 0.21,-0.05C10.18,2.14 11.07,2 12,2c0.65,0 1.29,0.07 1.91,0.19c0,0 0,0 0,0c0.25,0.05 0.5,0.11 0.75,0.18c0.07,0.02 0.14,0.03 0.22,0.06c0.19,0.06 0.38,0.13 0.57,0.2c0.12,0.05 0.25,0.09 0.37,0.14c0.14,0.06 0.27,0.12 0.4,0.18c0.17,0.08 0.34,0.16 0.51,0.25c0.08,0.04 0.15,0.09 0.23,0.14c0.21,0.12 0.42,0.24 0.62,0.38c0.01,0.01 0.03,0.02 0.04,0.03c0.51,0.35 0.99,0.74 1.45,1.19c0.24,0.24 0.47,0.49 0.68,0.75c0.04,0.04 0.06,0.09 0.1,0.13c0.17,0.22 0.34,0.45 0.5,0.68c0.01,0.01 0.02,0.03 0.03,0.04c0.69,1.05 1.17,2.21 1.42,3.44c0,0 0,0.01 0,0.01c0.06,0.29 0.1,0.58 0.13,0.87c0.01,0.04 0.01,0.09 0.02,0.13C21.98,11.32 22,11.66 22,12zM18.5,15c-1.93,0 -3.5,1.57 -3.5,3.5s1.57,3.5 3.5,3.5s3.5,-1.57 3.5,-3.5S20.43,15 18.5,15z"/>
|
||||
</vector>
|
128
res/layout/bubble_preference.xml
Normal file
128
res/layout/bubble_preference.xml
Normal file
@@ -0,0 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2020 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/notification_importance_toggle_marginTop"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- If bubbles is managed by the admin this is used to inform the user. -->
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/notification_importance_button_padding"
|
||||
android:textAppearance="@style/TextAppearance.Small"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<com.android.settings.notification.NotificationButtonRelativeLayout
|
||||
android:id="@+id/bubble_all"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/notification_importance_button_padding"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
<ImageView
|
||||
android:id="@+id/bubble_all_icon"
|
||||
android:src="@drawable/ic_bubble_all"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
<TextView
|
||||
android:id="@+id/bubble_all_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:layout_toEndOf="@id/bubble_all_icon"
|
||||
android:layout_marginStart="@dimen/notification_importance_drawable_padding"
|
||||
android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
|
||||
android:text="@string/bubble_app_setting_all"/>
|
||||
</com.android.settings.notification.NotificationButtonRelativeLayout>
|
||||
|
||||
<com.android.settings.notification.NotificationButtonRelativeLayout
|
||||
android:id="@+id/bubble_selected"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/notification_importance_button_padding"
|
||||
android:layout_marginTop="@dimen/notification_importance_button_separation"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
<ImageView
|
||||
android:id="@+id/bubble_selected_icon"
|
||||
android:src="@drawable/ic_bubble_selected"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
<TextView
|
||||
android:id="@+id/bubble_selected_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:layout_toEndOf="@id/bubble_selected_icon"
|
||||
android:layout_marginStart="@dimen/notification_importance_drawable_padding"
|
||||
android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
|
||||
android:text="@string/bubble_app_setting_selected"/>
|
||||
</com.android.settings.notification.NotificationButtonRelativeLayout>
|
||||
|
||||
<com.android.settings.notification.NotificationButtonRelativeLayout
|
||||
android:id="@+id/bubble_none"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/notification_importance_button_padding"
|
||||
android:layout_marginTop="@dimen/notification_importance_button_separation"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
<ImageView
|
||||
android:id="@+id/bubble_none_icon"
|
||||
android:src="@drawable/ic_bubble_none"
|
||||
android:background="@android:color/transparent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
android:focusable="false"/>
|
||||
<TextView
|
||||
android:id="@+id/bubble_none_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:layout_toEndOf="@id/bubble_none_icon"
|
||||
android:layout_marginStart="@dimen/notification_importance_drawable_padding"
|
||||
android:textAppearance="@style/TextAppearance.NotificationImportanceButton.Unselected"
|
||||
android:text="@string/bubble_app_setting_none"/>
|
||||
</com.android.settings.notification.NotificationButtonRelativeLayout>
|
||||
|
||||
</LinearLayout>
|
BIN
res/raw-night/bubble_notification_animation.mp4
Normal file
BIN
res/raw-night/bubble_notification_animation.mp4
Normal file
Binary file not shown.
BIN
res/raw/bubble_notification_animation.mp4
Normal file
BIN
res/raw/bubble_notification_animation.mp4
Normal file
Binary file not shown.
@@ -13,22 +13,18 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/bubbles_app_toggle_title"
|
||||
android:key="app_bubble_notification_settings">
|
||||
|
||||
<com.android.settingslib.widget.LayoutPreference
|
||||
android:key="pref_app_header"
|
||||
android:layout="@layout/settings_entity_header"/>
|
||||
|
||||
<com.android.settingslib.RestrictedSwitchPreference
|
||||
<com.android.settings.notification.app.BubblePreference
|
||||
android:key="bubble_pref"
|
||||
android:title="@string/notification_bubbles_title"/>
|
||||
|
||||
<com.android.settingslib.widget.FooterPreference
|
||||
android:key="notification_bubbles_footer"
|
||||
android:title="@string/bubbles_feature_education"
|
||||
android:selectable="false" />
|
||||
android:title="@string/notification_bubbles_title"
|
||||
settings:allowDividerBelow="false"/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@@ -29,6 +29,14 @@
|
||||
<com.android.settings.notification.app.NotificationFooterPreference
|
||||
android:key="block_desc" />
|
||||
|
||||
<!--Bubbles -->
|
||||
<Preference
|
||||
android:key="bubble_pref_link"
|
||||
android:title="@string/notification_bubbles_title"
|
||||
android:icon="@drawable/ic_create_bubble"
|
||||
settings:controller="com.android.settings.notification.app.BubbleSummaryPreferenceController">
|
||||
</Preference>
|
||||
|
||||
<!-- Conversations added here -->
|
||||
<PreferenceCategory
|
||||
android:title="@string/conversations_category_title"
|
||||
@@ -73,10 +81,6 @@
|
||||
settings:useAdditionalSummary="true"
|
||||
android:order="1001"
|
||||
settings:restrictedSwitchSummary="@string/enabled_by_admin" />
|
||||
<Preference
|
||||
android:key="bubble_link_pref"
|
||||
android:title="@string/notification_bubbles_title"
|
||||
android:order="1002" />
|
||||
<Preference
|
||||
android:key="app_link"
|
||||
android:order="1003"
|
||||
|
38
res/xml/bubble_notification_settings.xml
Normal file
38
res/xml/bubble_notification_settings.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2020 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:settings="http://schemas.android.com/apk/res-auto"
|
||||
android:title="@string/bubbles_app_toggle_title"
|
||||
android:key="bubble_notification_settings">
|
||||
|
||||
<com.android.settings.widget.VideoPreference
|
||||
android:key="bubbles_illustration"
|
||||
android:title="@string/summary_placeholder"
|
||||
settings:animation="@raw/bubble_notification_animation"
|
||||
settings:controller="com.android.settings.widget.VideoPreferenceController"
|
||||
android:persistent="false" />
|
||||
|
||||
<!-- Notification bubbles -->
|
||||
<SwitchPreference
|
||||
android:key="global_notification_bubbles"
|
||||
android:icon="@drawable/ic_create_bubble"
|
||||
settings:allowDividerAbove="true"
|
||||
android:title="@string/notifications_bubble_setting_title"
|
||||
android:summary="@string/notifications_bubble_setting_description"
|
||||
settings:controller="com.android.settings.notification.BubbleNotificationPreferenceController"/>
|
||||
|
||||
</PreferenceScreen>
|
@@ -50,15 +50,23 @@
|
||||
</Preference>
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory
|
||||
<Preference
|
||||
android:key="conversations"
|
||||
android:title="@string/conversations_category_title"
|
||||
android:order="5">
|
||||
<Preference
|
||||
android:key="manage_conversations"
|
||||
android:title="@string/manage_conversations"
|
||||
android:fragment="com.android.settings.notification.app.ConversationListSettings"/>
|
||||
</PreferenceCategory>
|
||||
settings:allowDividerAbove="true"
|
||||
android:summary="@string/manage_conversations"
|
||||
android:order="6"
|
||||
android:fragment="com.android.settings.notification.app.ConversationListSettings"
|
||||
/>
|
||||
|
||||
<Preference
|
||||
android:key="notification_bubbles"
|
||||
android:title="@string/notification_bubbles_title"
|
||||
android:summary="@string/notifications_bubble_setting_on_summary"
|
||||
android:order="7"
|
||||
settings:controller="com.android.settings.notification.BubbleSummaryNotificationPreferenceController"
|
||||
android:fragment="com.android.settings.notification.BubbleNotificationSettings"
|
||||
/>
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="configure_notifications_lock"
|
||||
|
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
|
||||
|
||||
public class BubbleGlobalPreferenceController extends DeveloperOptionsPreferenceController
|
||||
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
|
||||
|
||||
@VisibleForTesting
|
||||
static final int ON = 1;
|
||||
@VisibleForTesting
|
||||
static final int OFF = 0;
|
||||
|
||||
public BubbleGlobalPreferenceController(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return Settings.Global.NOTIFICATION_BUBBLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
writeSetting((boolean) newValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
((SwitchPreference) mPreference).setChecked(isEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDeveloperOptionsSwitchDisabled() {
|
||||
super.onDeveloperOptionsSwitchDisabled();
|
||||
writeSetting(false /* isEnabled */);
|
||||
updateState(mPreference);
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.NOTIFICATION_BUBBLES, OFF) == ON;
|
||||
}
|
||||
|
||||
private void writeSetting(boolean isEnabled) {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.NOTIFICATION_BUBBLES, isEnabled ? ON : OFF);
|
||||
}
|
||||
}
|
@@ -508,7 +508,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
|
||||
controllers.add(new DesktopModePreferenceController(context));
|
||||
controllers.add(new SizeCompatFreeformPreferenceController(context));
|
||||
controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
|
||||
controllers.add(new BubbleGlobalPreferenceController(context));
|
||||
controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
|
||||
controllers.add(new DefaultLaunchPreferenceController(context, "running_apps"));
|
||||
controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode"));
|
||||
|
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.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 androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Feature level screen for bubbles, available through notification menu.
|
||||
* Allows user to turn bubbles on or off for the device.
|
||||
*/
|
||||
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(getPreferenceKey());
|
||||
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.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, ON) == ON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setChecked(boolean isChecked) {
|
||||
return Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, isChecked ? ON : OFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSliceable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
class SettingObserver extends ContentObserver {
|
||||
|
||||
private final Uri NOTIFICATION_BUBBLES_URI =
|
||||
Settings.Global.getUriFor(NOTIFICATION_BUBBLES);
|
||||
|
||||
private final Preference mPreference;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.OnActivityResultListener;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.search.BaseSearchIndexProvider;
|
||||
import com.android.settingslib.search.SearchIndexable;
|
||||
|
||||
@SearchIndexable
|
||||
public class BubbleNotificationSettings extends DashboardFragment implements
|
||||
OnActivityResultListener {
|
||||
private static final String TAG = "BubbleNotiSettings";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return SettingsEnums.BUBBLE_SETTINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return TAG;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return R.xml.bubble_notification_settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* For Search.
|
||||
*/
|
||||
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
||||
new BaseSearchIndexProvider(R.xml.bubble_notification_settings);
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
|
||||
/**
|
||||
* Summary of the feature setting for bubbles, available through notification menu.
|
||||
*/
|
||||
public class BubbleSummaryNotificationPreferenceController extends BasePreferenceController {
|
||||
|
||||
@VisibleForTesting
|
||||
static final int ON = 1;
|
||||
|
||||
public BubbleSummaryNotificationPreferenceController(Context context, String preferenceKey) {
|
||||
super(context, preferenceKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
return mContext.getString(
|
||||
areBubblesEnabled()
|
||||
? R.string.notifications_bubble_setting_on_summary
|
||||
: R.string.switch_off_text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
return AVAILABLE;
|
||||
}
|
||||
|
||||
private boolean areBubblesEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, ON) == ON;
|
||||
}
|
||||
}
|
@@ -24,6 +24,7 @@ import android.app.INotificationManager;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationHistory;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.role.RoleManager;
|
||||
import android.app.usage.IUsageStatsManager;
|
||||
import android.app.usage.UsageEvents;
|
||||
@@ -81,7 +82,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.bubblePreference = getBubblePreference(row.pkg, row.uid);
|
||||
row.userId = UserHandle.getUserId(row.uid);
|
||||
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
|
||||
row.channelCount = getChannelCount(row.pkg, row.uid);
|
||||
@@ -192,18 +193,18 @@ public class NotificationBackend {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canBubble(String pkg, int uid) {
|
||||
public int getBubblePreference(String pkg, int uid) {
|
||||
try {
|
||||
return sINM.areBubblesAllowedForPackage(pkg, uid);
|
||||
return sINM.getBubblePreferenceForPackage(pkg, uid);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Error calling NoMan", e);
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setAllowBubbles(String pkg, int uid, boolean allow) {
|
||||
public boolean setAllowBubbles(String pkg, int uid, int preference) {
|
||||
try {
|
||||
sINM.setBubblesAllowed(pkg, uid, allow);
|
||||
sINM.setBubblesAllowed(pkg, uid, preference);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Error calling NoMan", e);
|
||||
@@ -563,7 +564,7 @@ public class NotificationBackend {
|
||||
public boolean systemApp;
|
||||
public boolean lockedImportance;
|
||||
public boolean showBadge;
|
||||
public boolean allowBubbles;
|
||||
public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
public int userId;
|
||||
public int blockedChannelCount;
|
||||
public int channelCount;
|
||||
|
@@ -30,6 +30,9 @@ import com.android.settingslib.search.SearchIndexable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* App level settings for bubbles.
|
||||
*/
|
||||
@SearchIndexable
|
||||
public class AppBubbleNotificationSettings extends NotificationSettings implements
|
||||
GlobalBubblePermissionObserverMixin.Listener {
|
||||
|
@@ -22,6 +22,10 @@ import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
@@ -29,10 +33,6 @@ import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
/** These settings are per app, so should not be returned in global search results. */
|
||||
public class AppNotificationSettings extends NotificationSettings {
|
||||
private static final String TAG = "AppNotificationSettings";
|
||||
@@ -41,8 +41,7 @@ public class AppNotificationSettings extends NotificationSettings {
|
||||
private static String KEY_ADVANCED_CATEGORY = "app_advanced";
|
||||
private static String KEY_BADGE = "badge";
|
||||
private static String KEY_APP_LINK = "app_link";
|
||||
private static String KEY_BUBBLE = "bubble_link_pref";
|
||||
private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK, KEY_BUBBLE};
|
||||
private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK};
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -121,9 +120,9 @@ public class AppNotificationSettings extends NotificationSettings {
|
||||
mControllers.add(new DescriptionPreferenceController(context));
|
||||
mControllers.add(new NotificationsOffPreferenceController(context));
|
||||
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
|
||||
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
|
||||
mControllers.add(new ChannelListPreferenceController(context, mBackend));
|
||||
mControllers.add(new AppConversationListPreferenceController(context, mBackend));
|
||||
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
|
||||
return new ArrayList<>(mControllers);
|
||||
}
|
||||
}
|
||||
|
172
src/com/android/settings/notification/app/BubblePreference.java
Normal file
172
src/com/android/settings/notification/app/BubblePreference.java
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.Utils;
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedPreferenceHelper;
|
||||
|
||||
/**
|
||||
* A tri-state preference allowing a user to specify what gets to bubble.
|
||||
*/
|
||||
public class BubblePreference extends Preference implements View.OnClickListener {
|
||||
RestrictedPreferenceHelper mHelper;
|
||||
|
||||
private int mSelectedPreference;
|
||||
|
||||
private Context mContext;
|
||||
private Drawable mSelectedBackground;
|
||||
private Drawable mUnselectedBackground;
|
||||
|
||||
private ButtonViewHolder mBubbleAllButton;
|
||||
private ButtonViewHolder mBubbleSelectedButton;
|
||||
private ButtonViewHolder mBubbleNoneButton;
|
||||
|
||||
public BubblePreference(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public BubblePreference(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BubblePreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public BubblePreference(Context context, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
mHelper = new RestrictedPreferenceHelper(context, this, attrs);
|
||||
mHelper.useAdminDisabledSummary(true);
|
||||
mContext = context;
|
||||
mSelectedBackground = mContext.getDrawable(R.drawable.button_border_selected);
|
||||
mUnselectedBackground = mContext.getDrawable(R.drawable.button_border_unselected);
|
||||
setLayoutResource(R.layout.bubble_preference);
|
||||
}
|
||||
|
||||
public void setSelectedPreference(int preference) {
|
||||
mSelectedPreference = preference;
|
||||
}
|
||||
|
||||
public int getSelectedPreference() {
|
||||
return mSelectedPreference;
|
||||
}
|
||||
|
||||
public void setDisabledByAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
|
||||
if (mHelper.setDisabledByAdmin(admin)) {
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
|
||||
final boolean disabledByAdmin = mHelper.isDisabledByAdmin();
|
||||
View summary = holder.findViewById(android.R.id.summary);
|
||||
if (disabledByAdmin) {
|
||||
mHelper.onBindViewHolder(holder);
|
||||
summary.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
summary.setVisibility(View.GONE);
|
||||
}
|
||||
holder.itemView.setClickable(false);
|
||||
|
||||
View bubbleAll = holder.findViewById(R.id.bubble_all);
|
||||
ImageView bubbleAllImage = (ImageView) holder.findViewById(R.id.bubble_all_icon);
|
||||
TextView bubbleAllText = (TextView) holder.findViewById(R.id.bubble_all_label);
|
||||
mBubbleAllButton = new ButtonViewHolder(bubbleAll, bubbleAllImage, bubbleAllText,
|
||||
BUBBLE_PREFERENCE_ALL);
|
||||
mBubbleAllButton.setSelected(mContext, mSelectedPreference == BUBBLE_PREFERENCE_ALL);
|
||||
bubbleAll.setTag(BUBBLE_PREFERENCE_ALL);
|
||||
bubbleAll.setOnClickListener(this);
|
||||
bubbleAll.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE);
|
||||
|
||||
View bubbleSelected = holder.findViewById(R.id.bubble_selected);
|
||||
ImageView bubbleSelectedImage = (ImageView) holder.findViewById(R.id.bubble_selected_icon);
|
||||
TextView bubbleSelectedText = (TextView) holder.findViewById(R.id.bubble_selected_label);
|
||||
mBubbleSelectedButton = new ButtonViewHolder(bubbleSelected, bubbleSelectedImage,
|
||||
bubbleSelectedText, BUBBLE_PREFERENCE_SELECTED);
|
||||
mBubbleSelectedButton.setSelected(mContext,
|
||||
mSelectedPreference == BUBBLE_PREFERENCE_SELECTED);
|
||||
bubbleSelected.setTag(BUBBLE_PREFERENCE_SELECTED);
|
||||
bubbleSelected.setOnClickListener(this);
|
||||
bubbleSelected.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE);
|
||||
|
||||
View bubbleNone = holder.findViewById(R.id.bubble_none);
|
||||
ImageView bubbleNoneImage = (ImageView) holder.findViewById(R.id.bubble_none_icon);
|
||||
TextView bubbleNoneText = (TextView) holder.findViewById(R.id.bubble_none_label);
|
||||
mBubbleNoneButton = new ButtonViewHolder(bubbleNone, bubbleNoneImage, bubbleNoneText,
|
||||
BUBBLE_PREFERENCE_NONE);
|
||||
mBubbleNoneButton.setSelected(mContext, mSelectedPreference == BUBBLE_PREFERENCE_NONE);
|
||||
bubbleNone.setTag(BUBBLE_PREFERENCE_NONE);
|
||||
bubbleNone.setOnClickListener(this);
|
||||
bubbleNone.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
final int selected = (int) v.getTag();
|
||||
callChangeListener(selected);
|
||||
|
||||
mBubbleAllButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_ALL);
|
||||
mBubbleSelectedButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_SELECTED);
|
||||
mBubbleNoneButton.setSelected(mContext, selected == BUBBLE_PREFERENCE_NONE);
|
||||
}
|
||||
|
||||
private class ButtonViewHolder {
|
||||
private View mView;
|
||||
private ImageView mImageView;
|
||||
private TextView mTextView;
|
||||
private int mId;
|
||||
|
||||
ButtonViewHolder(View v, ImageView iv, TextView tv, int identifier) {
|
||||
mView = v;
|
||||
mImageView = iv;
|
||||
mTextView = tv;
|
||||
mId = identifier;
|
||||
}
|
||||
|
||||
void setSelected(Context context, boolean selected) {
|
||||
mView.setBackground(selected ? mSelectedBackground : mUnselectedBackground);
|
||||
mView.setSelected(selected);
|
||||
|
||||
ColorStateList stateList = selected
|
||||
? Utils.getColorAccent(context)
|
||||
: Utils.getColorAttr(context, android.R.attr.textColorPrimary);
|
||||
mImageView.setImageTintList(stateList);
|
||||
mTextView.setTextColor(stateList);
|
||||
}
|
||||
}
|
||||
}
|
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
@@ -26,11 +27,14 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.PreferenceControllerMixin;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
|
||||
/**
|
||||
* Preference controller for Bubbles. This is used as the app-specific page and conversation
|
||||
* settings.
|
||||
*/
|
||||
public class BubblePreferenceController extends NotificationPreferenceController
|
||||
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
|
||||
|
||||
@@ -74,40 +78,49 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
if (mAppRow != null) {
|
||||
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
|
||||
if (mIsAppPage && mAppRow != null) {
|
||||
// We're on the app specific bubble page which displays a tri-state
|
||||
int backEndPref = mAppRow.bubblePreference;
|
||||
BubblePreference pref = (BubblePreference) preference;
|
||||
pref.setDisabledByAdmin(mAdmin);
|
||||
if (mChannel != null) {
|
||||
pref.setChecked(mChannel.canBubble() && isGloballyEnabled());
|
||||
pref.setEnabled(!pref.isDisabledByAdmin());
|
||||
if (!isGloballyEnabled()) {
|
||||
pref.setSelectedPreference(BUBBLE_PREFERENCE_NONE);
|
||||
} else {
|
||||
pref.setChecked(mAppRow.allowBubbles && isGloballyEnabled());
|
||||
pref.setSummary(mContext.getString(
|
||||
R.string.bubbles_app_toggle_summary, mAppRow.label));
|
||||
pref.setSelectedPreference(backEndPref);
|
||||
}
|
||||
} else if (mChannel != null) {
|
||||
// We're on the channel specific notification page which displays a toggle.
|
||||
RestrictedSwitchPreference switchpref = (RestrictedSwitchPreference) preference;
|
||||
switchpref.setDisabledByAdmin(mAdmin);
|
||||
switchpref.setChecked(mChannel.canBubble() && isGloballyEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean value = (Boolean) newValue && isGloballyEnabled();
|
||||
if (mChannel != null) {
|
||||
mChannel.setAllowBubbles(value);
|
||||
// Channel page is toggle
|
||||
mChannel.setAllowBubbles((boolean) newValue);
|
||||
saveChannel();
|
||||
return true;
|
||||
} else if (mAppRow != null && mFragmentManager != null) {
|
||||
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference;
|
||||
// if the global setting is off, toggling app level permission requires extra
|
||||
// confirmation
|
||||
if (!isGloballyEnabled() && !pref.isChecked()) {
|
||||
new BubbleWarningDialogFragment()
|
||||
.setPkgInfo(mAppRow.pkg, mAppRow.uid)
|
||||
.show(mFragmentManager, "dialog");
|
||||
return false;
|
||||
} else {
|
||||
mAppRow.allowBubbles = value;
|
||||
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value);
|
||||
} else if (mIsAppPage) {
|
||||
// App page is bubble preference
|
||||
BubblePreference pref = (BubblePreference) preference;
|
||||
if (mAppRow != null && mFragmentManager != null) {
|
||||
final int value = (int) newValue;
|
||||
if (!isGloballyEnabled()
|
||||
&& pref.getSelectedPreference() == BUBBLE_PREFERENCE_NONE) {
|
||||
// if the global setting is off, toggling app level permission requires extra
|
||||
// confirmation
|
||||
new BubbleWarningDialogFragment()
|
||||
.setPkgPrefInfo(mAppRow.pkg, mAppRow.uid, value)
|
||||
.show(mFragmentManager, "dialog");
|
||||
return false;
|
||||
} else {
|
||||
mAppRow.bubblePreference = value;
|
||||
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -118,21 +131,26 @@ public class BubblePreferenceController extends NotificationPreferenceController
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
|
||||
}
|
||||
|
||||
// Used in app level prompt that confirms the user is ok with turning on bubbles
|
||||
// globally. If they aren't, undo what
|
||||
/**
|
||||
* Used in app level prompt that confirms the user is ok with turning on bubbles
|
||||
* globally. If they aren't, undo that.
|
||||
*/
|
||||
public static void revertBubblesApproval(Context mContext, String pkg, int uid) {
|
||||
NotificationBackend backend = new NotificationBackend();
|
||||
backend.setAllowBubbles(pkg, uid, false);
|
||||
backend.setAllowBubbles(pkg, uid, BUBBLE_PREFERENCE_NONE);
|
||||
|
||||
// changing the global settings will cause the observer on the host page to reload
|
||||
// correct preference state
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
|
||||
}
|
||||
|
||||
// Apply global bubbles approval
|
||||
public static void applyBubblesApproval(Context mContext, String pkg, int uid) {
|
||||
/**
|
||||
* Apply global bubbles approval
|
||||
*/
|
||||
public static void applyBubblesApproval(Context mContext, String pkg, int uid, int pref) {
|
||||
NotificationBackend backend = new NotificationBackend();
|
||||
backend.setAllowBubbles(pkg, uid, true);
|
||||
backend.setAllowBubbles(pkg, uid, pref);
|
||||
// changing the global settings will cause the observer on the host page to reload
|
||||
// correct preference state
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,38 +16,34 @@
|
||||
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.AppInfoBase;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
public class BubbleSummaryPreferenceController extends NotificationPreferenceController {
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
/**
|
||||
* Summary of the app setting for bubbles, available through app notification settings.
|
||||
*/
|
||||
public class BubbleSummaryPreferenceController extends NotificationPreferenceController {
|
||||
private static final String KEY = "bubble_pref_link";
|
||||
|
||||
private static final String KEY = "bubble_link_pref";
|
||||
@VisibleForTesting
|
||||
static final int SYSTEM_WIDE_ON = 1;
|
||||
@VisibleForTesting
|
||||
static final int SYSTEM_WIDE_OFF = 0;
|
||||
static final int ON = 1;
|
||||
|
||||
public BubbleSummaryPreferenceController(Context context, NotificationBackend backend) {
|
||||
super(context, backend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (!super.isAvailable()) {
|
||||
@@ -63,45 +59,47 @@ public class BubbleSummaryPreferenceController extends NotificationPreferenceCon
|
||||
if (isDefaultChannel()) {
|
||||
return true;
|
||||
} else {
|
||||
return mAppRow != null && mAppRow.allowBubbles;
|
||||
return mAppRow != null;
|
||||
}
|
||||
}
|
||||
return isGloballyEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return KEY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState(Preference preference) {
|
||||
super.updateState(preference);
|
||||
|
||||
if (mAppRow != null) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
|
||||
args.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
|
||||
|
||||
preference.setIntent(new SubSettingLauncher(mContext)
|
||||
.setDestination(AppBubbleNotificationSettings.class.getName())
|
||||
.setArguments(args)
|
||||
.setSourceMetricsCategory(
|
||||
SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
|
||||
.toIntent());
|
||||
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, mAppRow.pkg);
|
||||
intent.putExtra(Settings.EXTRA_APP_UID, mAppRow.uid);
|
||||
preference.setIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
boolean canBubble = false;
|
||||
if (mAppRow != null) {
|
||||
if (mChannel != null) {
|
||||
canBubble |= mChannel.canBubble() && isGloballyEnabled();
|
||||
} else {
|
||||
canBubble |= mAppRow.allowBubbles && isGloballyEnabled();
|
||||
}
|
||||
if (mAppRow == null) {
|
||||
return null;
|
||||
}
|
||||
int backEndPref = mAppRow.bubblePreference;
|
||||
Resources res = mContext.getResources();
|
||||
if (backEndPref == BUBBLE_PREFERENCE_NONE || !isGloballyEnabled()) {
|
||||
return res.getString(R.string.bubble_app_setting_none);
|
||||
} else if (backEndPref == BUBBLE_PREFERENCE_ALL) {
|
||||
return res.getString(R.string.bubble_app_setting_all);
|
||||
} else {
|
||||
return res.getString(R.string.bubble_app_setting_selected);
|
||||
}
|
||||
return mContext.getString(canBubble ? R.string.switch_on_text : R.string.switch_off_text);
|
||||
}
|
||||
|
||||
private boolean isGloballyEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON;
|
||||
NOTIFICATION_BUBBLES, ON) == ON;
|
||||
}
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
|
||||
static final String KEY_PKG = "p";
|
||||
static final String KEY_UID = "u";
|
||||
static final String KEY_SELECTED_PREFERENCE = "pref";
|
||||
|
||||
|
||||
@Override
|
||||
@@ -34,10 +35,11 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
|
||||
return SettingsEnums.DIALOG_APP_BUBBLE_SETTINGS;
|
||||
}
|
||||
|
||||
public BubbleWarningDialogFragment setPkgInfo(String pkg, int uid) {
|
||||
public BubbleWarningDialogFragment setPkgPrefInfo(String pkg, int uid, int preference) {
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_PKG, pkg);
|
||||
args.putInt(KEY_UID, uid);
|
||||
args.putInt(KEY_SELECTED_PREFERENCE, preference);
|
||||
setArguments(args);
|
||||
return this;
|
||||
}
|
||||
@@ -48,6 +50,7 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
|
||||
final Bundle args = getArguments();
|
||||
final String pkg = args.getString(KEY_PKG);
|
||||
final int uid = args.getInt(KEY_UID);
|
||||
final int pref = args.getInt(KEY_SELECTED_PREFERENCE);
|
||||
|
||||
final String title =
|
||||
getResources().getString(R.string.bubbles_feature_disabled_dialog_title);
|
||||
@@ -60,7 +63,7 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
|
||||
.setPositiveButton(R.string.bubbles_feature_disabled_button_approve,
|
||||
(dialog, id) ->
|
||||
BubblePreferenceController.applyBubblesApproval(
|
||||
getContext(), pkg, uid))
|
||||
getContext(), pkg, uid, pref))
|
||||
.setNegativeButton(R.string.bubbles_feature_disabled_button_cancel,
|
||||
(dialog, id) ->
|
||||
BubblePreferenceController.revertBubblesApproval(
|
||||
|
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.development;
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import static com.android.settings.development.BubbleGlobalPreferenceController.OFF;
|
||||
import static com.android.settings.development.BubbleGlobalPreferenceController.ON;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BubbleGlobalPreferenceControllerTest {
|
||||
private Context mContext;
|
||||
|
||||
@Mock
|
||||
private SwitchPreference mPreference;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
|
||||
private BubbleGlobalPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new BubbleGlobalPreferenceController(mContext);
|
||||
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
|
||||
.thenReturn(mPreference);
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingEnabled_allowBubbles_shouldBeOn() {
|
||||
mController.onPreferenceChange(mPreference, true /* new value */);
|
||||
|
||||
assertThat(isSettingEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceChange_settingDisabled_allowBubbles_shouldBeOff() {
|
||||
mController.onPreferenceChange(mPreference, false /* new value */);
|
||||
|
||||
assertThat(isSettingEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingEnabled_preferenceShouldBeChecked() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, 1 /* enabled */);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingReset_defaultDisabled_preferenceShouldNotBeChecked() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, 0 /* enabled */);
|
||||
mController.updateState(mPreference);
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDeveloperOptionsSwitchDisabled_shouldDisable() {
|
||||
mController.onDeveloperOptionsSwitchDisabled();
|
||||
|
||||
verify(mPreference).setChecked(false);
|
||||
verify(mPreference).setEnabled(false);
|
||||
|
||||
assertThat(isSettingEnabled()).isFalse();
|
||||
}
|
||||
|
||||
private boolean isSettingEnabled() {
|
||||
return Settings.Global.getInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
OFF /* default off */) == ON;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
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.junit.Assert.assertEquals;
|
||||
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 androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
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;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BubbleNotificationPreferenceControllerTest {
|
||||
|
||||
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);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
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 getAvilabilityStatus_returnsAvailable() {
|
||||
assertEquals(AVAILABLE, mController.getAvailabilityStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingIsOn_preferenceSetChecked() {
|
||||
final TwoStatePreference preference = mock(TwoStatePreference.class);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
|
||||
|
||||
mController.updateState(preference);
|
||||
|
||||
verify(preference).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateState_settingIsOff_preferenceSetUnchecked() {
|
||||
final TwoStatePreference preference = mock(TwoStatePreference.class);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
|
||||
assertThat(Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, ON)).isEqualTo(OFF);
|
||||
|
||||
mController.updateState(preference);
|
||||
|
||||
verify(preference).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_settingIsOff_shouldReturnFalse() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
|
||||
|
||||
assertThat(mController.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_settingIsOn_shouldReturnTrue() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
|
||||
|
||||
assertThat(mController.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setFalse_disablesSetting() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
|
||||
|
||||
mController.setChecked(false);
|
||||
int updatedValue = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, -1);
|
||||
|
||||
assertThat(updatedValue).isEqualTo(OFF);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_setTrue_enablesSetting() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
|
||||
|
||||
mController.setChecked(true);
|
||||
int updatedValue = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
NOTIFICATION_BUBBLES, -1);
|
||||
|
||||
assertThat(updatedValue).isEqualTo(ON);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isSliceable_returnsFalse() {
|
||||
assertThat(mController.isSliceable()).isFalse();
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.notification;
|
||||
|
||||
import static android.provider.Settings.Global.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 android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BubbleSummaryNotificationPreferenceControllerTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
private BubbleSummaryNotificationPreferenceController mController;
|
||||
private Preference mPreference;
|
||||
|
||||
private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new BubbleSummaryNotificationPreferenceController(mContext,
|
||||
KEY_NOTIFICATION_BUBBLES);
|
||||
mPreference = new Preference(RuntimeEnvironment.application);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_NOTIFICATION_BUBBLESIsOff_returnOffString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, OFF);
|
||||
|
||||
assertThat(mController.getSummary()).isEqualTo("Off");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_NOTIFICATION_BUBBLESIsOff_returnOnString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, ON);
|
||||
|
||||
String onString = mContext.getString(R.string.notifications_bubble_setting_on_summary);
|
||||
assertThat(mController.getSummary()).isEqualTo(onString);
|
||||
}
|
||||
}
|
@@ -17,6 +17,9 @@
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
|
||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
@@ -25,8 +28,8 @@ import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_OFF;
|
||||
import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
@@ -44,6 +47,11 @@ import android.content.Context;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
import com.android.settingslib.RestrictedLockUtils;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
@@ -58,11 +66,6 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BubblePreferenceControllerTest {
|
||||
|
||||
@@ -125,7 +128,7 @@ public class BubblePreferenceControllerTest {
|
||||
public void testIsAvailable_channel_yesIfAppOff() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = false;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
|
||||
mController.onResume(appRow, channel, null, null, null, null);
|
||||
@@ -177,7 +180,7 @@ public class BubblePreferenceControllerTest {
|
||||
@Test
|
||||
public void testIsAvailable_defaultChannel() {
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
|
||||
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
|
||||
@@ -190,7 +193,7 @@ public class BubblePreferenceControllerTest {
|
||||
@Test
|
||||
public void testIsAvailable_channel() {
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
|
||||
mController.onResume(appRow, channel, null, null, null, null);
|
||||
@@ -213,7 +216,20 @@ public class BubblePreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState_channelNotBlockable() {
|
||||
public void testUpdateState_app_disabledByAdmin() {
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
when(channel.getId()).thenReturn("something");
|
||||
mAppPageController.onResume(new NotificationBackend.AppRow(), channel, null,
|
||||
null, null, mock(RestrictedLockUtils.EnforcedAdmin.class));
|
||||
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
mAppPageController.updateState(pref);
|
||||
|
||||
assertFalse(pref.isEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState_channel_channelNotBlockable() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
@@ -251,21 +267,24 @@ public class BubblePreferenceControllerTest {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.label = "App!";
|
||||
appRow.allowBubbles = true;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
mController.updateState(pref);
|
||||
assertTrue(pref.isChecked());
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
mAppPageController.updateState(pref);
|
||||
assertEquals(BUBBLE_PREFERENCE_ALL, pref.getSelectedPreference());
|
||||
|
||||
appRow.allowBubbles = false;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
mController.updateState(pref);
|
||||
assertFalse(pref.isChecked());
|
||||
mAppPageController.updateState(pref);
|
||||
assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference());
|
||||
|
||||
assertNotNull(pref.getSummary());
|
||||
assertTrue(pref.getSummary().toString().contains(appRow.label));
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
mAppPageController.updateState(pref);
|
||||
assertEquals(BUBBLE_PREFERENCE_SELECTED, pref.getSelectedPreference());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -274,22 +293,21 @@ public class BubblePreferenceControllerTest {
|
||||
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.label = "App!";
|
||||
appRow.allowBubbles = true;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
mController.updateState(pref);
|
||||
assertFalse(pref.isChecked());
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
mAppPageController.updateState(pref);
|
||||
assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_on_channel() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
|
||||
channel.setAllowBubbles(false);
|
||||
mController.onResume(appRow, channel, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
@@ -306,10 +324,9 @@ public class BubblePreferenceControllerTest {
|
||||
public void testOnPreferenceChange_off_channel() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
|
||||
channel.setAllowBubbles(true);
|
||||
mController.onResume(appRow, channel, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
@@ -322,59 +339,78 @@ public class BubblePreferenceControllerTest {
|
||||
assertFalse(channel.canBubble());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_on_app() {
|
||||
public void testOnPreferenceChange_app_all() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = false;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
|
||||
mController.displayPreference(mScreen);
|
||||
mController.updateState(pref);
|
||||
mAppPageController.displayPreference(mScreen);
|
||||
mAppPageController.updateState(pref);
|
||||
|
||||
mController.onPreferenceChange(pref, true);
|
||||
mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL);
|
||||
|
||||
assertTrue(appRow.allowBubbles);
|
||||
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(true));
|
||||
assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_ALL);
|
||||
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_off_app() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
mController.onResume(appRow, null, null, 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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_on_app_offGlobally() {
|
||||
public void testOnPreferenceChange_app_all_offGlobally() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_OFF);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = false;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
|
||||
mController.displayPreference(mScreen);
|
||||
mController.updateState(pref);
|
||||
mAppPageController.displayPreference(mScreen);
|
||||
mAppPageController.updateState(pref);
|
||||
|
||||
mController.onPreferenceChange(pref, true);
|
||||
mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL);
|
||||
|
||||
assertFalse(appRow.allowBubbles);
|
||||
verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(true));
|
||||
assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_NONE);
|
||||
verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL));
|
||||
verify(mFragmentManager, times(1)).beginTransaction();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_app_selected() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
|
||||
mAppPageController.displayPreference(mScreen);
|
||||
mAppPageController.updateState(pref);
|
||||
|
||||
mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_NONE);
|
||||
|
||||
assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference);
|
||||
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPreferenceChange_app_none() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mAppPageController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
BubblePreference pref = new BubblePreference(mContext);
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
|
||||
mAppPageController.displayPreference(mScreen);
|
||||
mAppPageController.updateState(pref);
|
||||
|
||||
mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_NONE);
|
||||
|
||||
assertEquals(BUBBLE_PREFERENCE_NONE, appRow.bubblePreference);
|
||||
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_NONE));
|
||||
}
|
||||
}
|
||||
|
@@ -17,28 +17,31 @@
|
||||
package com.android.settings.notification.app;
|
||||
|
||||
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
|
||||
import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED;
|
||||
import static android.app.NotificationManager.IMPORTANCE_HIGH;
|
||||
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
|
||||
|
||||
import static com.android.settings.notification.app.BubbleSummaryPreferenceController.SYSTEM_WIDE_OFF;
|
||||
import static com.android.settings.notification.app.BubbleSummaryPreferenceController.SYSTEM_WIDE_ON;
|
||||
import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_OFF;
|
||||
import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
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.app.NotificationChannel;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -50,8 +53,6 @@ import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowApplication;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BubbleSummaryPreferenceControllerTest {
|
||||
|
||||
@@ -70,13 +71,13 @@ public class BubbleSummaryPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoCrashIfNoOnResume() {
|
||||
public void isAvailable_noOnResume_shouldNotCrash() {
|
||||
mController.isAvailable();
|
||||
mController.updateState(mock(Preference.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAvailable_notIfAppBlocked() {
|
||||
public void isAvailable_appBlocked_shouldReturnFalse() {
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.banned = true;
|
||||
mController.onResume(appRow, mock(NotificationChannel.class), null, null, null, null);
|
||||
@@ -84,53 +85,52 @@ public class BubbleSummaryPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@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, null, null);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_OFF);
|
||||
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAvailable_app() {
|
||||
public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOn_shouldReturnTrue() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
|
||||
assertTrue(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNotAvailable_app_globalOff() {
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOff_shouldReturnFalse() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_OFF);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAvailable_defaultChannel() {
|
||||
public void isAvailable_nonNullChannelNOTIFICATION_BUBBLESisOff_shouldReturnFalse() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_OFF);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
NotificationChannel channel = mock(NotificationChannel.class);
|
||||
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
|
||||
mController.onResume(appRow, channel, null, null, null, null);
|
||||
|
||||
assertFalse(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_defaultChannelNOTIFICATION_BUBBLESisOn_shouldReturnTrue() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
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, null, null);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
|
||||
assertTrue(mController.isAvailable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateState() {
|
||||
public void updateState_setsIntent() {
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
Preference pref = new Preference(mContext);
|
||||
@@ -139,22 +139,53 @@ public class BubbleSummaryPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSummary() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.allowBubbles = true;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
assertEquals("On", mController.getSummary());
|
||||
|
||||
public void getSummary_NOTIFICATION_BUBBLESIsOff_returnsNoneString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_OFF);
|
||||
assertEquals("Off", mController.getSummary());
|
||||
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
|
||||
appRow.allowBubbles = false;
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
assertEquals("Off", mController.getSummary());
|
||||
String noneString = mContext.getString(R.string.bubble_app_setting_none);
|
||||
assertEquals(noneString, mController.getSummary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_BUBBLE_PREFERENCE_NONEisSelected_returnsNoneString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_ON);
|
||||
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
String noneString = mContext.getString(R.string.bubble_app_setting_none);
|
||||
assertEquals(noneString, mController.getSummary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_BUBBLE_PREFERENCE_ALLisSelected_returnsAllString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_ON);
|
||||
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
String allString = mContext.getString(R.string.bubble_app_setting_all);
|
||||
assertEquals(allString, mController.getSummary());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSummary_BUBBLE_PREFERENCE_SELECTEDisSelected_returnsSelectedString() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
|
||||
SYSTEM_WIDE_ON);
|
||||
|
||||
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
|
||||
appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
|
||||
mController.onResume(appRow, null, null, null, null, null);
|
||||
|
||||
String selectedString = mContext.getString(R.string.bubble_app_setting_selected);
|
||||
assertEquals(selectedString, mController.getSummary());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user