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:
Mady Mellor
2020-03-05 18:31:39 -08:00
parent 23040a3b2b
commit 409c3dfe68
29 changed files with 1183 additions and 411 deletions

View File

@@ -20,6 +20,6 @@
android:color="@color/notification_importance_selection_bg" /> android:color="@color/notification_importance_selection_bg" />
<stroke <stroke
android:width="2dp" android:width="2dp"
android:color="@color/notification_importance_button_selected"/> android:color="?android:attr/colorAccent"/>
<corners android:radius="@dimen/rect_button_radius" /> <corners android:radius="@dimen/rect_button_radius" />
</shape> </shape>

View 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>

View 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>

View 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>

View File

@@ -14,17 +14,13 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24" android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal"> android:tint="?android:attr/colorControlNormal">
<path <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="#FF000000"
android:fillColor="#000000"/> 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"/>
<path </vector>
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"/>
</vector>

View 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>

Binary file not shown.

Binary file not shown.

View File

@@ -13,22 +13,18 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto" xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/bubbles_app_toggle_title" android:title="@string/bubbles_app_toggle_title"
android:key="app_bubble_notification_settings"> android:key="app_bubble_notification_settings">
<com.android.settingslib.widget.LayoutPreference <com.android.settingslib.widget.LayoutPreference
android:key="pref_app_header" android:key="pref_app_header"
android:layout="@layout/settings_entity_header"/> android:layout="@layout/settings_entity_header"/>
<com.android.settingslib.RestrictedSwitchPreference <com.android.settings.notification.app.BubblePreference
android:key="bubble_pref" android:key="bubble_pref"
android:title="@string/notification_bubbles_title"/> android:title="@string/notification_bubbles_title"
settings:allowDividerBelow="false"/>
<com.android.settingslib.widget.FooterPreference
android:key="notification_bubbles_footer"
android:title="@string/bubbles_feature_education"
android:selectable="false" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -29,6 +29,14 @@
<com.android.settings.notification.app.NotificationFooterPreference <com.android.settings.notification.app.NotificationFooterPreference
android:key="block_desc" /> 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 --> <!-- Conversations added here -->
<PreferenceCategory <PreferenceCategory
android:title="@string/conversations_category_title" android:title="@string/conversations_category_title"
@@ -73,10 +81,6 @@
settings:useAdditionalSummary="true" settings:useAdditionalSummary="true"
android:order="1001" android:order="1001"
settings:restrictedSwitchSummary="@string/enabled_by_admin" /> settings:restrictedSwitchSummary="@string/enabled_by_admin" />
<Preference
android:key="bubble_link_pref"
android:title="@string/notification_bubbles_title"
android:order="1002" />
<Preference <Preference
android:key="app_link" android:key="app_link"
android:order="1003" android:order="1003"

View 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>

View File

@@ -50,15 +50,23 @@
</Preference> </Preference>
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory <Preference
android:key="conversations" android:key="conversations"
android:title="@string/conversations_category_title" android:title="@string/conversations_category_title"
android:order="5"> settings:allowDividerAbove="true"
<Preference android:summary="@string/manage_conversations"
android:key="manage_conversations" android:order="6"
android:title="@string/manage_conversations" android:fragment="com.android.settings.notification.app.ConversationListSettings"
android:fragment="com.android.settings.notification.app.ConversationListSettings"/> />
</PreferenceCategory>
<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 <PreferenceCategory
android:key="configure_notifications_lock" android:key="configure_notifications_lock"

View File

@@ -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);
}
}

View File

@@ -508,7 +508,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new DesktopModePreferenceController(context)); controllers.add(new DesktopModePreferenceController(context));
controllers.add(new SizeCompatFreeformPreferenceController(context)); controllers.add(new SizeCompatFreeformPreferenceController(context));
controllers.add(new ShortcutManagerThrottlingPreferenceController(context)); controllers.add(new ShortcutManagerThrottlingPreferenceController(context));
controllers.add(new BubbleGlobalPreferenceController(context));
controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context)); controllers.add(new EnableGnssRawMeasFullTrackingPreferenceController(context));
controllers.add(new DefaultLaunchPreferenceController(context, "running_apps")); controllers.add(new DefaultLaunchPreferenceController(context, "running_apps"));
controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode")); controllers.add(new DefaultLaunchPreferenceController(context, "demo_mode"));

View File

@@ -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);
}
}
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -24,6 +24,7 @@ import android.app.INotificationManager;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.app.NotificationChannelGroup; import android.app.NotificationChannelGroup;
import android.app.NotificationHistory; import android.app.NotificationHistory;
import android.app.NotificationManager;
import android.app.role.RoleManager; import android.app.role.RoleManager;
import android.app.usage.IUsageStatsManager; import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents; import android.app.usage.UsageEvents;
@@ -81,7 +82,7 @@ public class NotificationBackend {
row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app); row.icon = IconDrawableFactory.newInstance(context).getBadgedIcon(app);
row.banned = getNotificationsBanned(row.pkg, row.uid); row.banned = getNotificationsBanned(row.pkg, row.uid);
row.showBadge = canShowBadge(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.userId = UserHandle.getUserId(row.uid);
row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid); row.blockedChannelCount = getBlockedChannelCount(row.pkg, row.uid);
row.channelCount = getChannelCount(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 { try {
return sINM.areBubblesAllowedForPackage(pkg, uid); return sINM.getBubblePreferenceForPackage(pkg, uid);
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Error calling NoMan", 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 { try {
sINM.setBubblesAllowed(pkg, uid, allow); sINM.setBubblesAllowed(pkg, uid, preference);
return true; return true;
} catch (Exception e) { } catch (Exception e) {
Log.w(TAG, "Error calling NoMan", e); Log.w(TAG, "Error calling NoMan", e);
@@ -563,7 +564,7 @@ public class NotificationBackend {
public boolean systemApp; public boolean systemApp;
public boolean lockedImportance; public boolean lockedImportance;
public boolean showBadge; public boolean showBadge;
public boolean allowBubbles; public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE;
public int userId; public int userId;
public int blockedChannelCount; public int blockedChannelCount;
public int channelCount; public int channelCount;

View File

@@ -30,6 +30,9 @@ import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/**
* App level settings for bubbles.
*/
@SearchIndexable @SearchIndexable
public class AppBubbleNotificationSettings extends NotificationSettings implements public class AppBubbleNotificationSettings extends NotificationSettings implements
GlobalBubblePermissionObserverMixin.Listener { GlobalBubblePermissionObserverMixin.Listener {

View File

@@ -22,6 +22,10 @@ import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; 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.internal.widget.LockPatternUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -29,10 +33,6 @@ import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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. */ /** These settings are per app, so should not be returned in global search results. */
public class AppNotificationSettings extends NotificationSettings { public class AppNotificationSettings extends NotificationSettings {
private static final String TAG = "AppNotificationSettings"; 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_ADVANCED_CATEGORY = "app_advanced";
private static String KEY_BADGE = "badge"; private static String KEY_BADGE = "badge";
private static String KEY_APP_LINK = "app_link"; 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};
private static String[] LEGACY_NON_ADVANCED_KEYS = {KEY_BADGE, KEY_APP_LINK, KEY_BUBBLE};
@Override @Override
public int getMetricsCategory() { public int getMetricsCategory() {
@@ -121,9 +120,9 @@ public class AppNotificationSettings extends NotificationSettings {
mControllers.add(new DescriptionPreferenceController(context)); mControllers.add(new DescriptionPreferenceController(context));
mControllers.add(new NotificationsOffPreferenceController(context)); mControllers.add(new NotificationsOffPreferenceController(context));
mControllers.add(new DeletedChannelsPreferenceController(context, mBackend)); mControllers.add(new DeletedChannelsPreferenceController(context, mBackend));
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
mControllers.add(new ChannelListPreferenceController(context, mBackend)); mControllers.add(new ChannelListPreferenceController(context, mBackend));
mControllers.add(new AppConversationListPreferenceController(context, mBackend)); mControllers.add(new AppConversationListPreferenceController(context, mBackend));
mControllers.add(new BubbleSummaryPreferenceController(context, mBackend));
return new ArrayList<>(mControllers); return new ArrayList<>(mControllers);
} }
} }

View 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);
}
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification.app; package com.android.settings.notification.app;
import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE;
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; import static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -26,11 +27,14 @@ import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.RestrictedSwitchPreference; 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 public class BubblePreferenceController extends NotificationPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener { implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener {
@@ -74,40 +78,49 @@ public class BubblePreferenceController extends NotificationPreferenceController
return true; return true;
} }
@Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
if (mAppRow != null) { if (mIsAppPage && mAppRow != null) {
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; // We're on the app specific bubble page which displays a tri-state
int backEndPref = mAppRow.bubblePreference;
BubblePreference pref = (BubblePreference) preference;
pref.setDisabledByAdmin(mAdmin); pref.setDisabledByAdmin(mAdmin);
if (mChannel != null) { if (!isGloballyEnabled()) {
pref.setChecked(mChannel.canBubble() && isGloballyEnabled()); pref.setSelectedPreference(BUBBLE_PREFERENCE_NONE);
pref.setEnabled(!pref.isDisabledByAdmin());
} else { } else {
pref.setChecked(mAppRow.allowBubbles && isGloballyEnabled()); pref.setSelectedPreference(backEndPref);
pref.setSummary(mContext.getString(
R.string.bubbles_app_toggle_summary, mAppRow.label));
} }
} 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 @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean value = (Boolean) newValue && isGloballyEnabled();
if (mChannel != null) { if (mChannel != null) {
mChannel.setAllowBubbles(value); // Channel page is toggle
mChannel.setAllowBubbles((boolean) newValue);
saveChannel(); saveChannel();
return true; } else if (mIsAppPage) {
} else if (mAppRow != null && mFragmentManager != null) { // App page is bubble preference
RestrictedSwitchPreference pref = (RestrictedSwitchPreference) preference; BubblePreference pref = (BubblePreference) preference;
// if the global setting is off, toggling app level permission requires extra if (mAppRow != null && mFragmentManager != null) {
// confirmation final int value = (int) newValue;
if (!isGloballyEnabled() && !pref.isChecked()) { if (!isGloballyEnabled()
new BubbleWarningDialogFragment() && pref.getSelectedPreference() == BUBBLE_PREFERENCE_NONE) {
.setPkgInfo(mAppRow.pkg, mAppRow.uid) // if the global setting is off, toggling app level permission requires extra
.show(mFragmentManager, "dialog"); // confirmation
return false; new BubbleWarningDialogFragment()
} else { .setPkgPrefInfo(mAppRow.pkg, mAppRow.uid, value)
mAppRow.allowBubbles = value; .show(mFragmentManager, "dialog");
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value); return false;
} else {
mAppRow.bubblePreference = value;
mBackend.setAllowBubbles(mAppRow.pkg, mAppRow.uid, value);
}
} }
} }
return true; return true;
@@ -118,21 +131,26 @@ public class BubblePreferenceController extends NotificationPreferenceController
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON; 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) { public static void revertBubblesApproval(Context mContext, String pkg, int uid) {
NotificationBackend backend = new NotificationBackend(); 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 // changing the global settings will cause the observer on the host page to reload
// correct preference state // correct preference state
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); 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(); 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 // changing the global settings will cause the observer on the host page to reload
// correct preference state // correct preference state
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.putInt(mContext.getContentResolver(),

View File

@@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,38 +16,34 @@
package com.android.settings.notification.app; 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 static android.provider.Settings.Global.NOTIFICATION_BUBBLES;
import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.content.Intent;
import android.content.res.Resources;
import android.provider.Settings; 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.annotation.VisibleForTesting;
import androidx.preference.Preference; 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 @VisibleForTesting
static final int SYSTEM_WIDE_ON = 1; static final int ON = 1;
@VisibleForTesting
static final int SYSTEM_WIDE_OFF = 0;
public BubbleSummaryPreferenceController(Context context, NotificationBackend backend) { public BubbleSummaryPreferenceController(Context context, NotificationBackend backend) {
super(context, backend); super(context, backend);
} }
@Override
public String getPreferenceKey() {
return KEY;
}
@Override @Override
public boolean isAvailable() { public boolean isAvailable() {
if (!super.isAvailable()) { if (!super.isAvailable()) {
@@ -63,45 +59,47 @@ public class BubbleSummaryPreferenceController extends NotificationPreferenceCon
if (isDefaultChannel()) { if (isDefaultChannel()) {
return true; return true;
} else { } else {
return mAppRow != null && mAppRow.allowBubbles; return mAppRow != null;
} }
} }
return isGloballyEnabled(); return isGloballyEnabled();
} }
@Override
public String getPreferenceKey() {
return KEY;
}
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
super.updateState(preference); super.updateState(preference);
if (mAppRow != null) { if (mAppRow != null) {
Bundle args = new Bundle(); final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
args.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg); intent.putExtra(Settings.EXTRA_APP_PACKAGE, mAppRow.pkg);
args.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid); intent.putExtra(Settings.EXTRA_APP_UID, mAppRow.uid);
preference.setIntent(intent);
preference.setIntent(new SubSettingLauncher(mContext)
.setDestination(AppBubbleNotificationSettings.class.getName())
.setArguments(args)
.setSourceMetricsCategory(
SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
.toIntent());
} }
} }
@Override @Override
public CharSequence getSummary() { public CharSequence getSummary() {
boolean canBubble = false; if (mAppRow == null) {
if (mAppRow != null) { return null;
if (mChannel != null) { }
canBubble |= mChannel.canBubble() && isGloballyEnabled(); int backEndPref = mAppRow.bubblePreference;
} else { Resources res = mContext.getResources();
canBubble |= mAppRow.allowBubbles && isGloballyEnabled(); 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() { private boolean isGloballyEnabled() {
return Settings.Global.getInt(mContext.getContentResolver(), return Settings.Global.getInt(mContext.getContentResolver(),
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF) == SYSTEM_WIDE_ON; NOTIFICATION_BUBBLES, ON) == ON;
} }
} }

View File

@@ -27,6 +27,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
public class BubbleWarningDialogFragment extends InstrumentedDialogFragment { public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
static final String KEY_PKG = "p"; static final String KEY_PKG = "p";
static final String KEY_UID = "u"; static final String KEY_UID = "u";
static final String KEY_SELECTED_PREFERENCE = "pref";
@Override @Override
@@ -34,10 +35,11 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
return SettingsEnums.DIALOG_APP_BUBBLE_SETTINGS; 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(); Bundle args = new Bundle();
args.putString(KEY_PKG, pkg); args.putString(KEY_PKG, pkg);
args.putInt(KEY_UID, uid); args.putInt(KEY_UID, uid);
args.putInt(KEY_SELECTED_PREFERENCE, preference);
setArguments(args); setArguments(args);
return this; return this;
} }
@@ -48,6 +50,7 @@ public class BubbleWarningDialogFragment extends InstrumentedDialogFragment {
final Bundle args = getArguments(); final Bundle args = getArguments();
final String pkg = args.getString(KEY_PKG); final String pkg = args.getString(KEY_PKG);
final int uid = args.getInt(KEY_UID); final int uid = args.getInt(KEY_UID);
final int pref = args.getInt(KEY_SELECTED_PREFERENCE);
final String title = final String title =
getResources().getString(R.string.bubbles_feature_disabled_dialog_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, .setPositiveButton(R.string.bubbles_feature_disabled_button_approve,
(dialog, id) -> (dialog, id) ->
BubblePreferenceController.applyBubblesApproval( BubblePreferenceController.applyBubblesApproval(
getContext(), pkg, uid)) getContext(), pkg, uid, pref))
.setNegativeButton(R.string.bubbles_feature_disabled_button_cancel, .setNegativeButton(R.string.bubbles_feature_disabled_button_cancel,
(dialog, id) -> (dialog, id) ->
BubblePreferenceController.revertBubblesApproval( BubblePreferenceController.revertBubblesApproval(

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -17,6 +17,9 @@
package com.android.settings.notification.app; package com.android.settings.notification.app;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; 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_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_NONE; 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_OFF;
import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON; 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.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
@@ -44,6 +47,11 @@ import android.content.Context;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; 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.settings.notification.NotificationBackend;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
@@ -58,11 +66,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication; 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) @RunWith(RobolectricTestRunner.class)
public class BubblePreferenceControllerTest { public class BubblePreferenceControllerTest {
@@ -125,7 +128,7 @@ public class BubblePreferenceControllerTest {
public void testIsAvailable_channel_yesIfAppOff() { public void testIsAvailable_channel_yesIfAppOff() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null, null, null); mController.onResume(appRow, channel, null, null, null, null);
@@ -177,7 +180,7 @@ public class BubblePreferenceControllerTest {
@Test @Test
public void testIsAvailable_defaultChannel() { public void testIsAvailable_defaultChannel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID); when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
@@ -190,7 +193,7 @@ public class BubblePreferenceControllerTest {
@Test @Test
public void testIsAvailable_channel() { public void testIsAvailable_channel() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
mController.onResume(appRow, channel, null, null, null, null); mController.onResume(appRow, channel, null, null, null, null);
@@ -213,7 +216,20 @@ public class BubblePreferenceControllerTest {
} }
@Test @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); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
@@ -251,21 +267,24 @@ public class BubblePreferenceControllerTest {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.label = "App!"; appRow.label = "App!";
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mController.onResume(appRow, null, null, null, null, null); mAppPageController.onResume(appRow, null, null, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); BubblePreference pref = new BubblePreference(mContext);
mController.updateState(pref); mAppPageController.updateState(pref);
assertTrue(pref.isChecked()); assertEquals(BUBBLE_PREFERENCE_ALL, pref.getSelectedPreference());
appRow.allowBubbles = false; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
mController.onResume(appRow, null, null, null, null, null); mAppPageController.onResume(appRow, null, null, null, null, null);
mController.updateState(pref); mAppPageController.updateState(pref);
assertFalse(pref.isChecked()); assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference());
assertNotNull(pref.getSummary()); appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
assertTrue(pref.getSummary().toString().contains(appRow.label)); mAppPageController.onResume(appRow, null, null, null, null, null);
mAppPageController.updateState(pref);
assertEquals(BUBBLE_PREFERENCE_SELECTED, pref.getSelectedPreference());
} }
@Test @Test
@@ -274,22 +293,21 @@ public class BubblePreferenceControllerTest {
NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF); NOTIFICATION_BUBBLES, SYSTEM_WIDE_OFF);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.label = "App!"; appRow.label = "App!";
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mController.onResume(appRow, null, null, null, null, null); mAppPageController.onResume(appRow, null, null, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); BubblePreference pref = new BubblePreference(mContext);
mController.updateState(pref); mAppPageController.updateState(pref);
assertFalse(pref.isChecked()); assertEquals(BUBBLE_PREFERENCE_NONE, pref.getSelectedPreference());
} }
@Test @Test
public void testOnPreferenceChange_on_channel() { public void testOnPreferenceChange_on_channel() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
NotificationChannel channel = NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW); new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_LOW);
channel.setAllowBubbles(false);
mController.onResume(appRow, channel, null, null, null, null); mController.onResume(appRow, channel, null, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
@@ -306,10 +324,9 @@ public class BubblePreferenceControllerTest {
public void testOnPreferenceChange_off_channel() { public void testOnPreferenceChange_off_channel() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_SELECTED;
NotificationChannel channel = NotificationChannel channel =
new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH); new NotificationChannel(DEFAULT_CHANNEL_ID, "a", IMPORTANCE_HIGH);
channel.setAllowBubbles(true);
mController.onResume(appRow, channel, null, null, null, null); mController.onResume(appRow, channel, null, null, null, null);
RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext); RestrictedSwitchPreference pref = new RestrictedSwitchPreference(mContext);
@@ -322,59 +339,78 @@ public class BubblePreferenceControllerTest {
assertFalse(channel.canBubble()); assertFalse(channel.canBubble());
} }
@Test @Test
public void testOnPreferenceChange_on_app() { public void testOnPreferenceChange_app_all() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
mController.onResume(appRow, null, null, null, null, null); 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); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen); mAppPageController.displayPreference(mScreen);
mController.updateState(pref); mAppPageController.updateState(pref);
mController.onPreferenceChange(pref, true); mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL);
assertTrue(appRow.allowBubbles); assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_ALL);
verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(true)); verify(mBackend, times(1)).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL));
} }
@Test @Test
public void testOnPreferenceChange_off_app() { public void testOnPreferenceChange_app_all_offGlobally() {
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() {
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
SYSTEM_WIDE_OFF); SYSTEM_WIDE_OFF);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = false; appRow.bubblePreference = BUBBLE_PREFERENCE_NONE;
mController.onResume(appRow, null, null, null, null, null); 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); when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(pref);
mController.displayPreference(mScreen); mAppPageController.displayPreference(mScreen);
mController.updateState(pref); mAppPageController.updateState(pref);
mController.onPreferenceChange(pref, true); mAppPageController.onPreferenceChange(pref, BUBBLE_PREFERENCE_ALL);
assertFalse(appRow.allowBubbles); assertEquals(appRow.bubblePreference, BUBBLE_PREFERENCE_NONE);
verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(true)); verify(mBackend, never()).setAllowBubbles(any(), anyInt(), eq(BUBBLE_PREFERENCE_ALL));
verify(mFragmentManager, times(1)).beginTransaction(); 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));
}
} }

View File

@@ -17,28 +17,31 @@
package com.android.settings.notification.app; package com.android.settings.notification.app;
import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; 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_HIGH;
import static android.provider.Settings.Global.NOTIFICATION_BUBBLES; 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.BubblePreferenceController.SYSTEM_WIDE_OFF;
import static com.android.settings.notification.app.BubbleSummaryPreferenceController.SYSTEM_WIDE_ON; import static com.android.settings.notification.app.BubblePreferenceController.SYSTEM_WIDE_ON;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; 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.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.NotificationChannel; import android.app.NotificationChannel;
import android.content.Context; import android.content.Context;
import android.provider.Settings; import android.provider.Settings;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.notification.NotificationBackend; import com.android.settings.notification.NotificationBackend;
import org.junit.Before; import org.junit.Before;
@@ -50,8 +53,6 @@ import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowApplication;
import androidx.preference.Preference;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class BubbleSummaryPreferenceControllerTest { public class BubbleSummaryPreferenceControllerTest {
@@ -70,13 +71,13 @@ public class BubbleSummaryPreferenceControllerTest {
} }
@Test @Test
public void testNoCrashIfNoOnResume() { public void isAvailable_noOnResume_shouldNotCrash() {
mController.isAvailable(); mController.isAvailable();
mController.updateState(mock(Preference.class)); mController.updateState(mock(Preference.class));
} }
@Test @Test
public void testIsAvailable_notIfAppBlocked() { public void isAvailable_appBlocked_shouldReturnFalse() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.banned = true; appRow.banned = true;
mController.onResume(appRow, mock(NotificationChannel.class), null, null, null, null); mController.onResume(appRow, mock(NotificationChannel.class), null, null, null, null);
@@ -84,53 +85,52 @@ public class BubbleSummaryPreferenceControllerTest {
} }
@Test @Test
public void testIsAvailable_notIfOffGlobally() { public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOn_shouldReturnTrue() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
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() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null, null, null); mController.onResume(appRow, null, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
assertTrue(mController.isAvailable()); assertTrue(mController.isAvailable());
} }
@Test @Test
public void testIsNotAvailable_app_globalOff() { public void isAvailable_nullChannelNOTIFICATION_BUBBLESisOff_shouldReturnFalse() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
SYSTEM_WIDE_OFF); SYSTEM_WIDE_OFF);
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
mController.onResume(appRow, null, null, null, null, null);
assertFalse(mController.isAvailable()); assertFalse(mController.isAvailable());
} }
@Test @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(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true;
NotificationChannel channel = mock(NotificationChannel.class); NotificationChannel channel = mock(NotificationChannel.class);
when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH); when(channel.getImportance()).thenReturn(IMPORTANCE_HIGH);
when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID); when(channel.getId()).thenReturn(DEFAULT_CHANNEL_ID);
mController.onResume(appRow, channel, null, null, null, null); mController.onResume(appRow, channel, null, null, null, null);
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON);
assertTrue(mController.isAvailable()); assertTrue(mController.isAvailable());
} }
@Test @Test
public void testUpdateState() { public void updateState_setsIntent() {
NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
appRow.allowBubbles = true; appRow.bubblePreference = BUBBLE_PREFERENCE_ALL;
mController.onResume(appRow, null, null, null, null, null); mController.onResume(appRow, null, null, null, null, null);
Preference pref = new Preference(mContext); Preference pref = new Preference(mContext);
@@ -139,22 +139,53 @@ public class BubbleSummaryPreferenceControllerTest {
} }
@Test @Test
public void testGetSummary() { public void getSummary_NOTIFICATION_BUBBLESIsOff_returnsNoneString() {
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());
Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, Settings.Global.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES,
SYSTEM_WIDE_OFF); SYSTEM_WIDE_OFF);
assertEquals("Off", mController.getSummary());
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); 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());
} }
} }