Add display and notif vis effects to new modes ui

Test: atest com.android.settings.notification.modes
Flag: android.app.modes_ui
Fixes: 337087926
Fixes: 308820151
Change-Id: Id9cd9cc4b2d521713a2ba1d4581eb818ad0e5eee
This commit is contained in:
Julia Reynolds
2024-05-21 16:59:40 -04:00
parent 3f2bcf973a
commit 4a84e58dba
18 changed files with 1508 additions and 15 deletions

View File

@@ -7948,8 +7948,55 @@
<!-- Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] --> <!-- Do not disturb: Subtitle for the Visual signals option to toggle on/off visual signals/alerts when the screen is on/when screen is off. [CHAR LIMIT=30] -->
<string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string> <string name="zen_mode_visual_signals_settings_subtitle">Allow visual signals</string>
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
<string name="mode_interruption_filter_title">Notifications that can reach you</string>
<!-- Do not disturb: mode page section title [CHAR LIMIT=80] -->
<string name="mode_device_effects_title">Additional actions</string>
<!-- Do not disturb: display settings title [CHAR LIMIT=80] -->
<string name="mode_display_settings_title">Display settings</string>
<!-- Do not disturb: display options section header [CHAR LIMIT=80] -->
<string name="mode_display_options_section">Display options</string>
<!-- Do not disturb: device effect option, title or first in list [CHAR LIMIT=80] -->
<string name="mode_grayscale_title">Grayscale</string>
<!-- Do not disturb: device effect option, not first in list [CHAR LIMIT=80] -->
<string name="mode_grayscale_title_secondary_list">grayscale</string>
<!-- Do not disturb: device effect summary [CHAR LIMIT=NONE] -->
<string name="mode_grayscale_summary">Change the screen to black and white</string>
<!-- Do not disturb: device effect option, title or first in list [CHAR LIMIT=80] -->
<string name="mode_aod_title">Keep the screen dark</string>
<!-- Do not disturb: device effect option, not first in list [CHAR LIMIT=80] -->
<string name="mode_aod_title_secondary_list">keep the screen dark</string>
<!-- Do not disturb: device effect summary [CHAR LIMIT=NONE] -->
<string name="mode_aod_summary">Disable always on display</string>
<!-- Do not disturb: device effect option , title or first in list[CHAR LIMIT=80] -->
<string name="mode_wallpaper_title">Dim the wallpaper</string>
<!-- Do not disturb: device effect option, not first in list [CHAR LIMIT=80] -->
<string name="mode_wallpaper_title_secondary_list">dim the wallpaper</string>
<!-- Do not disturb: device effect summary [CHAR LIMIT=NONE] -->
<string name="mode_wallpaper_summary">Filter the brightness of the wallpaper</string>
<!-- Do not disturb: device effect option, title or first in list [CHAR LIMIT=80] -->
<string name="mode_dark_theme_title">Enable dark theme</string>
<!-- Do not disturb: device effect option, not first in list [CHAR LIMIT=80] -->
<string name="mode_dark_theme_title_secondary_list">enable dark theme</string>
<!-- Do not disturb: device effect summary [CHAR LIMIT=NONE] -->
<string name="mode_dark_theme_summary">Switch the OS and apps to prefer light text on a dark
background, which may be easier on the eyes and confers significant battery savings on some devices</string>
<!-- [CHAR LIMIT=NONE] Zen mode settings: Summary for sound interruption settings -->
<string name="mode_display_settings_summary">
{count, plural, offset:2
=0 {No display changes}
=1 {{effect_1}}
=2 {{effect_1} and {effect_2}}
=3 {{effect_1}, {effect_2}, and {effect_3}}
other {{effect_1}, {effect_2}, and # more}
}
</string>
<!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] --> <!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
<string name="zen_mode_restrict_notifications_title">Display options for hidden notifications</string> <string name="zen_mode_restrict_notifications_title">Display options for filtered
notifications</string>
<!-- Do not disturb: Hide notifications screen category title [CHAR LIMIT=100] --> <!-- Do not disturb: Hide notifications screen category title [CHAR LIMIT=100] -->
<string name="zen_mode_restrict_notifications_category">When Do Not Disturb is on</string> <string name="zen_mode_restrict_notifications_category">When Do Not Disturb is on</string>
<!-- Do not disturb: Mute notifications option [CHAR LIMIT=60] --> <!-- Do not disturb: Mute notifications option [CHAR LIMIT=60] -->
@@ -7971,11 +8018,12 @@
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] --> <!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_disable_custom">Remove custom setting</string> <string name="zen_mode_restrict_notifications_disable_custom">Remove custom setting</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] --> <!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_summary_muted">No sound from notifications</string> <string name="zen_mode_restrict_notifications_summary_muted">Notifications shown</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] --> <!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=60] -->
<string name="zen_mode_restrict_notifications_summary_custom">Partially hidden</string> <string name="zen_mode_restrict_notifications_summary_custom">Notifications partially
hidden</string>
<!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=100] --> <!-- Do not disturb: restrict notifications page, menu option [CHAR LIMIT=100] -->
<string name="zen_mode_restrict_notifications_summary_hidden">No visuals or sound from notifications</string> <string name="zen_mode_restrict_notifications_summary_hidden">Notifications hidden</string>
<!-- Do not disturb: what to block title [CHAR LIMIT = 60] --> <!-- Do not disturb: what to block title [CHAR LIMIT = 60] -->
<string name="zen_mode_what_to_block_title">Custom restrictions</string> <string name="zen_mode_what_to_block_title">Custom restrictions</string>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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"
android:title="@string/mode_display_settings_title">
<PreferenceCategory
android:key="display_options"
android:title="@string/mode_display_options_section">
<Preference
android:key="notification_visibility"
android:title="@string/zen_mode_restrict_notifications_title" />
<SwitchPreferenceCompat
android:key="effect_greyscale"
android:title="@string/mode_grayscale_title"
android:summary="@string/mode_grayscale_summary"/>
<SwitchPreferenceCompat
android:key="effect_aod"
android:title="@string/mode_aod_title"
android:summary="@string/mode_aod_summary"/>
<SwitchPreferenceCompat
android:key="effect_wallpaper"
android:title="@string/mode_wallpaper_title"
android:summary="@string/mode_wallpaper_summary"/>
<SwitchPreferenceCompat
android:key="effect_dark_theme"
android:title="@string/mode_dark_theme_title"
android:summary="@string/mode_dark_theme_summary"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 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"
android:key="zen_mode_block_settings_page"
android:title="@string/zen_mode_restrict_notifications_title">
<PreferenceCategory
android:title="@string/zen_mode_block_effects_screen_off"
android:key="zen_mode_block_screen_off">
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_intent"
android:title="@string/zen_mode_block_effect_intent" />
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_light"
android:title="@string/zen_mode_block_effect_light" />
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_ambient"
android:title="@string/zen_mode_block_effect_ambient" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/zen_mode_block_effects_screen_on"
android:key="zen_mode_block_screen_on">
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_badge"
android:title="@string/zen_mode_block_effect_badge" />
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_status"
android:title="@string/zen_mode_block_effect_status" />
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_peek"
android:title="@string/zen_mode_block_effect_peek" />
<com.android.settings.widget.DisabledCheckBoxPreference
android:key="zen_effect_list"
android:title="@string/zen_mode_block_effect_list" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -22,6 +22,9 @@
android:key="header" android:key="header"
android:layout="@layout/settings_entity_header" /> android:layout="@layout/settings_entity_header" />
<PreferenceCategory
android:title="@string/mode_interruption_filter_title"
android:key="modes_filters">
<Preference <Preference
android:key="zen_mode_people" android:key="zen_mode_people"
android:title="@string/zen_category_people"/> android:title="@string/zen_category_people"/>
@@ -29,5 +32,13 @@
<Preference <Preference
android:key="zen_other_settings" android:key="zen_other_settings"
android:title="@string/zen_category_exceptions" /> android:title="@string/zen_category_exceptions" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/mode_device_effects_title"
android:key="modes_additional_actions">
<Preference
android:key="mode_display_settings"
android:title="@string/mode_display_settings_title" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -90,7 +90,7 @@ class ZenMode {
.build(); .build();
private final String mId; private final String mId;
private final AutomaticZenRule mRule; private AutomaticZenRule mRule;
private final boolean mIsActive; private final boolean mIsActive;
private final boolean mIsManualDnd; private final boolean mIsManualDnd;
@@ -190,6 +190,14 @@ class ZenMode {
} }
} }
/**
* Use sparingly. If you're updating a policy field, use
* {@link #setPolicy(android.service.notification.ZenPolicy)} instead.
*/
public void setAzr(@NonNull AutomaticZenRule newRule) {
mRule = newRule;
}
/** /**
* Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the * Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
* supplied policy. In some cases this involves conversions, so that the following call * supplied policy. In some cases this involves conversions, so that the following call

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2024 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.modes;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {
public ZenModeDisplayEffectPreferenceController(Context context, String key,
ZenModesBackend backend) {
super(context, key, backend);
}
@Override
public void updateState(Preference preference) {
TwoStatePreference pref = (TwoStatePreference) preference;
ZenDeviceEffects effects = getMode().getRule().getDeviceEffects();
if (effects == null) {
pref.setChecked(false);
} else {
switch (getPreferenceKey()) {
case "effect_greyscale":
pref.setChecked(effects.shouldDisplayGrayscale());
break;
case "effect_aod":
pref.setChecked(effects.shouldSuppressAmbientDisplay());
break;
case "effect_wallpaper":
pref.setChecked(effects.shouldDimWallpaper());
break;
case "effect_dark_theme":
pref.setChecked(effects.shouldUseNightMode());
break;
}
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean allow = (Boolean) newValue;
ZenDeviceEffects currEffects = getMode().getRule().getDeviceEffects();
ZenDeviceEffects.Builder updatedEffects = currEffects == null
? new ZenDeviceEffects.Builder()
: new ZenDeviceEffects.Builder(getMode().getRule().getDeviceEffects());
switch (getPreferenceKey()) {
case "effect_greyscale":
updatedEffects.setShouldDisplayGrayscale(allow);
break;
case "effect_aod":
updatedEffects.setShouldSuppressAmbientDisplay(allow);
break;
case "effect_wallpaper":
updatedEffects.setShouldDimWallpaper(allow);
break;
case "effect_dark_theme":
updatedEffects.setShouldUseNightMode(allow);
break;
}
AutomaticZenRule updatedAzr = new AutomaticZenRule.Builder(getMode().getRule())
.setDeviceEffects(updatedEffects.build())
.build();
getMode().setAzr(updatedAzr);
mBackend.updateMode(getMode());
return true;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 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.modes;
import android.app.settings.SettingsEnums;
import android.content.Context;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
/**
* Settings page that shows what device effects/notification visuals will change when this mode
* is on.
*/
public class ZenModeDisplayFragment extends ZenModeFragmentBase {
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeNotifVisLinkPreferenceController(
context, "notification_visibility", mBackend));
prefControllers.add(new ZenModeDisplayEffectPreferenceController(
context, "effect_greyscale", mBackend));
prefControllers.add(new ZenModeDisplayEffectPreferenceController(
context, "effect_aod", mBackend));
prefControllers.add(new ZenModeDisplayEffectPreferenceController(
context, "effect_wallpaper", mBackend));
prefControllers.add(new ZenModeDisplayEffectPreferenceController(
context, "effect_dark_theme", mBackend));
return prefControllers;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.modes_display_settings;
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - make this the correct metrics category
return SettingsEnums.DND_PEOPLE;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2024 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.modes;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
import android.os.Bundle;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
public class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController {
ZenModeSummaryHelper mSummaryHelper;
public ZenModeDisplayLinkPreferenceController(Context context, String key,
ZenModesBackend backend) {
super(context, key, backend);
mSummaryHelper = new ZenModeSummaryHelper(context, backend);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, getMode().getId());
// TODO(b/332937635): Update metrics category
preference.setIntent(new SubSettingLauncher(mContext)
.setDestination(ZenModeDisplayFragment.class.getName())
.setSourceMetricsCategory(0)
.setArguments(bundle)
.toIntent());
}
@Override
public CharSequence getSummary() {
return mSummaryHelper.getDisplayEffectsSummary(getMode());
}
}

View File

@@ -35,14 +35,14 @@ public class ZenModeFragment extends ZenModeFragmentBase {
@Override @Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
// TODO: fill in with all the elements of this page. Each should be an instance of
// {@link AbstractZenModePreferenceController}.
List<AbstractPreferenceController> prefControllers = new ArrayList<>(); List<AbstractPreferenceController> prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend)); prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController( prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend)); context, "zen_mode_people", mBackend));
prefControllers.add(new ZenModeOtherLinkPreferenceController( prefControllers.add(new ZenModeOtherLinkPreferenceController(
context, "zen_other_settings", mBackend)); context, "zen_other_settings", mBackend));
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
context, "mode_display_settings", mBackend));
return prefControllers; return prefControllers;
} }

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2024 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.modes;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.service.notification.ZenPolicy;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
/**
* Settings page that shows what notification visuals will change when this mode is on.
*/
public class ZenModeNotifVisFragment extends ZenModeFragmentBase {
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
List<AbstractPreferenceController> prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_intent", ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, null, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_light", ZenPolicy.VISUAL_EFFECT_LIGHTS, null, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_peek", ZenPolicy.VISUAL_EFFECT_PEEK, null, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_status", ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
new int[] {ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_badge", ZenPolicy.VISUAL_EFFECT_BADGE, null, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_ambient", ZenPolicy.VISUAL_EFFECT_AMBIENT, null, mBackend));
prefControllers.add(new ZenModeNotifVisPreferenceController(context,
"zen_effect_list", ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, null, mBackend));
return prefControllers;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.modes_notif_vis_settings;
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - make this the correct metrics category
return SettingsEnums.DND_PEOPLE;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 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.modes;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
import android.os.Bundle;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
public class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController {
ZenModeSummaryHelper mSummaryBuilder;
public ZenModeNotifVisLinkPreferenceController(Context context, String key,
ZenModesBackend backend) {
super(context, key, backend);
mSummaryBuilder = new ZenModeSummaryHelper(context, backend);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, getMode().getId());
// TODO(b/332937635): Update metrics category
preference.setIntent(new SubSettingLauncher(mContext)
.setDestination(ZenModeNotifVisFragment.class.getName())
.setSourceMetricsCategory(0)
.setArguments(bundle)
.toIntent());
}
@Override
public CharSequence getSummary() {
return mSummaryBuilder.getBlockedEffectsSummary(getMode());
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright (C) 2024 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.modes;
import android.content.Context;
import android.service.notification.ZenPolicy;
import androidx.annotation.VisibleForTesting;
import androidx.preference.CheckBoxPreference;
import androidx.preference.Preference;
import com.android.settings.widget.DisabledCheckBoxPreference;
public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferenceController
implements Preference.OnPreferenceChangeListener {
@VisibleForTesting protected @ZenPolicy.VisualEffect int mEffect;
// if any of these effects are suppressed, this effect must be too
@VisibleForTesting protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
public ZenModeNotifVisPreferenceController(Context context, String key,
@ZenPolicy.VisualEffect int visualEffect,
@ZenPolicy.VisualEffect int[] parentSuppressedEffects, ZenModesBackend backend) {
super(context, key, backend);
mEffect = visualEffect;
mParentSuppressedEffects = parentSuppressedEffects;
}
@Override
public boolean isAvailable() {
if (!super.isAvailable()) {
return false;
}
if (mEffect == ZenPolicy.VISUAL_EFFECT_LIGHTS) {
return mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
}
return true;
}
@Override
public void updateState(Preference preference) {
boolean suppressed = !getMode().getPolicy().isVisualEffectAllowed(mEffect, false);
boolean parentSuppressed = false;
if (mParentSuppressedEffects != null) {
for (@ZenPolicy.VisualEffect int parentEffect : mParentSuppressedEffects) {
if (!getMode().getPolicy().isVisualEffectAllowed(parentEffect, true)) {
parentSuppressed = true;
}
}
}
if (parentSuppressed) {
((CheckBoxPreference) preference).setChecked(true);
onPreferenceChange(preference, true);
((DisabledCheckBoxPreference) preference).enableCheckbox(false);
} else {
((DisabledCheckBoxPreference) preference).enableCheckbox(true);
((CheckBoxPreference) preference).setChecked(suppressed);
}
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean allowEffect = !((Boolean) newValue);
if (getMode().getPolicy().isVisualEffectAllowed(mEffect, true) != allowEffect) {
ZenPolicy diffPolicy = new ZenPolicy.Builder()
.showVisualEffect(mEffect, allowEffect)
.build();
getMode().setPolicy(diffPolicy);
mBackend.updateMode(getMode());
}
return true;
}
}

View File

@@ -30,11 +30,21 @@ import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS; import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS; import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM; import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
import android.content.Context; import android.content.Context;
import android.icu.text.MessageFormat; import android.icu.text.MessageFormat;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy; import android.service.notification.ZenPolicy;
import android.util.SparseArray;
import com.android.settings.R; import com.android.settings.R;
import java.util.ArrayList; import java.util.ArrayList;
@@ -129,10 +139,22 @@ public class ZenModeSummaryHelper {
} }
String getBlockedEffectsSummary(ZenMode zenMode) { String getBlockedEffectsSummary(ZenMode zenMode) {
if (zenMode.getPolicy().shouldShowAllVisualEffects()) { List<Integer> relevantVisualEffects = new ArrayList<>();
relevantVisualEffects.add(VISUAL_EFFECT_FULL_SCREEN_INTENT);
relevantVisualEffects.add(VISUAL_EFFECT_PEEK);
relevantVisualEffects.add(VISUAL_EFFECT_STATUS_BAR);
relevantVisualEffects.add(VISUAL_EFFECT_BADGE);
relevantVisualEffects.add(VISUAL_EFFECT_AMBIENT);
relevantVisualEffects.add(VISUAL_EFFECT_NOTIFICATION_LIST);
if (mContext.getResources()
.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
relevantVisualEffects.add(VISUAL_EFFECT_LIGHTS);
}
if (shouldShowAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
return mContext.getResources().getString( return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_muted); R.string.zen_mode_restrict_notifications_summary_muted);
} else if (zenMode.getPolicy().shouldHideAllVisualEffects()) { } else if (shouldHideAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
return mContext.getResources().getString( return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_hidden); R.string.zen_mode_restrict_notifications_summary_hidden);
} else { } else {
@@ -141,6 +163,89 @@ public class ZenModeSummaryHelper {
} }
} }
private boolean shouldShowAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects) {
for (int i = 0; i < relevantEffects.size(); i++) {
if (!policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
return false;
}
}
return true;
}
private boolean shouldHideAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects) {
for (int i = 0; i < relevantEffects.size(); i++) {
if (policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
return false;
}
}
return true;
}
String getDisplayEffectsSummary(ZenMode zenMode) {
boolean isFirst = true;
List<String> enabledEffects = new ArrayList<>();
if (!zenMode.getPolicy().shouldShowAllVisualEffects()) {
enabledEffects.add(getBlockedEffectsSummary(zenMode));
isFirst = false;
}
ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
if (currEffects != null) {
if (currEffects.shouldDisplayGrayscale()) {
if (isFirst) {
enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
} else {
enabledEffects.add(mContext.getString(
R.string.mode_grayscale_title_secondary_list));
}
isFirst = false;
}
if (currEffects.shouldSuppressAmbientDisplay()) {
if (isFirst) {
enabledEffects.add(mContext.getString(R.string.mode_aod_title));
} else {
enabledEffects.add(mContext.getString(
R.string.mode_aod_title_secondary_list));
}
isFirst = false;
}
if (currEffects.shouldDimWallpaper()) {
if (isFirst) {
enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
} else {
enabledEffects.add(mContext.getString(
R.string.mode_wallpaper_title_secondary_list));
}
isFirst = false;
}
if (currEffects.shouldUseNightMode()) {
if (isFirst) {
enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
} else {
enabledEffects.add(mContext.getString(
R.string.mode_dark_theme_title_secondary_list));
}
isFirst = false;
}
}
int numCategories = enabledEffects.size();
MessageFormat msgFormat = new MessageFormat(
mContext.getString(R.string.mode_display_settings_summary),
Locale.getDefault());
Map<String, Object> args = new HashMap<>();
args.put("count", numCategories);
if (numCategories >= 1) {
args.put("effect_1", enabledEffects.get(0));
if (numCategories >= 2) {
args.put("effect_2", enabledEffects.get(1));
if (numCategories == 3) {
args.put("effect_3", enabledEffects.get(2));
}
}
}
return msgFormat.format(args);
}
private List<String> getEnabledCategories(ZenPolicy policy, private List<String> getEnabledCategories(ZenPolicy policy,
Predicate<Integer> filteredCategories, boolean capitalizeFirstInList) { Predicate<Integer> filteredCategories, boolean capitalizeFirstInList) {
List<String> enabledCategories = new ArrayList<>(); List<String> enabledCategories = new ArrayList<>();

View File

@@ -0,0 +1,252 @@
/*
* Copyright (C) 2024 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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.STATE_UNSET;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public final class ZenModeDisplayEffectPreferenceControllerTest {
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
}
@Test
public void testUpdateState_grayscale() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(
mContext, "effect_greyscale", mBackend);
controller.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testOnPreferenceChange_grayscale() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(mContext, "effect_greyscale", mBackend);
controller.updateZenMode(preference, zenMode);
controller.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getRule().getDeviceEffects().shouldDisplayGrayscale())
.isFalse();
}
@Test
public void testUpdateState_aod() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
controller.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testOnPreferenceChange_aod() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldSuppressAmbientDisplay(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
controller.updateZenMode(preference, zenMode);
controller.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getRule().getDeviceEffects().shouldSuppressAmbientDisplay())
.isFalse();
}
@Test
public void testUpdateState_wallpaper() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(
mContext, "effect_wallpaper", mBackend);
controller.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testOnPreferenceChange_wallpaper() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(
mContext, "effect_wallpaper", mBackend);
controller.updateZenMode(preference, zenMode);
controller.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getRule().getDeviceEffects().shouldDimWallpaper()).isFalse();
}
@Test
public void testUpdateState_darkTheme() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
mBackend);
controller.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testOnPreferenceChange_darkTheme() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldUseNightMode(true)
.build())
.build(), true);
ZenModeDisplayEffectPreferenceController controller =
new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
mBackend);
controller.updateZenMode(preference, zenMode);
controller.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getRule().getDeviceEffects().shouldUseNightMode()).isFalse();
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2024 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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import org.junit.Before;
import org.junit.Rule;
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 final class ZenModeDisplayLinkPreferenceControllerTest {
private ZenModeDisplayLinkPreferenceController mController;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeDisplayLinkPreferenceController(
mContext, "something", mBackend);
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
public void testHasSummary() {
Preference pref = mock(Preference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build(), true);
mController.updateZenMode(pref, zenMode);
verify(pref).setSummary(any());
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2024 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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenPolicy;
import androidx.preference.Preference;
import org.junit.Before;
import org.junit.Rule;
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 final class ZenModeNotifVisLinkPreferenceControllerTest {
private ZenModeNotifVisLinkPreferenceController mController;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeNotifVisLinkPreferenceController(
mContext, "something", mBackend);
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
public void testHasSummary() {
Preference pref = mock(Preference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build(), true);
mController.updateZenMode(pref, zenMode);
verify(pref).setSummary(any());
}
}

View File

@@ -0,0 +1,241 @@
/*
* Copyright (C) 2024 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.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.STATE_ALLOW;
import static android.service.notification.ZenPolicy.STATE_DISALLOW;
import static android.service.notification.ZenPolicy.STATE_UNSET;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.content.res.Resources;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import androidx.preference.TwoStatePreference;
import com.android.settings.notification.zen.ZenModeVisEffectPreferenceController;
import com.android.settings.widget.DisabledCheckBoxPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public final class ZenModeNotifVisPreferenceControllerTest {
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ZenModeNotifVisPreferenceController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeNotifVisPreferenceController(mContext,
"zen_effect_peek", VISUAL_EFFECT_PEEK, null, mBackend);
}
@Test
public void isAvailable() {
// SUPPRESSED_EFFECT_PEEK is always available:
assertThat(mController.isAvailable()).isTrue();
// SUPPRESSED_EFFECT_LIGHTS is only available if the device has an LED:
Context mockContext = mock(Context.class);
mController = new ZenModeNotifVisPreferenceController(mockContext,
"zen_effect_light", VISUAL_EFFECT_LIGHTS, null, mBackend);
Resources mockResources = mock(Resources.class);
when(mockContext.getResources()).thenReturn(mockResources);
when(mockResources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed))
.thenReturn(false); // no light
assertThat(mController.isAvailable()).isFalse();
when(mockResources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed))
.thenReturn(true); // has light
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void updateState_notChecked() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showAllVisualEffects()
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false);
verify(preference).enableCheckbox(true);
}
@Test
public void updateState_checked() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showVisualEffect(VISUAL_EFFECT_PEEK, false)
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
verify(preference).enableCheckbox(true);
}
@Test
public void updateState_checkedFalse_parentChecked() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
mController = new ZenModeNotifVisPreferenceController(mContext,
"zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false)
.showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true)
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
verify(preference).enableCheckbox(false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getPolicy().getVisualEffectStatusBar())
.isEqualTo(STATE_DISALLOW);
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
.isEqualTo(STATE_UNSET);
}
@Test
public void updateState_checkedFalse_parentNotChecked() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
mController = new ZenModeNotifVisPreferenceController(mContext,
"zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showAllVisualEffects()
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false);
verify(preference).enableCheckbox(true);
verify(mBackend, never()).updateMode(any());
}
@Test
public void onPreferenceChanged_checkedFalse() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.hideAllVisualEffects()
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
mController.onPreferenceChange(preference, false);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
.isEqualTo(STATE_ALLOW);
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
.isEqualTo(STATE_UNSET);
}
@Test
public void onPreferenceChanged_checkedTrue() {
DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showAllVisualEffects()
.build())
.build(), true);
mController.updateZenMode(preference, zenMode);
mController.onPreferenceChange(preference, true);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
.isEqualTo(STATE_DISALLOW);
assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
.isEqualTo(STATE_UNSET);
}
}

View File

@@ -20,11 +20,14 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule; import android.app.AutomaticZenRule;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy; import android.service.notification.ZenPolicy;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -165,4 +168,161 @@ public class ZenModesSummaryHelperTest {
assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo( assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
"Alarms, media, and 3 more can interrupt"); "Alarms, media, and 3 more can interrupt");
} }
@Test
public void getBlockedEffectsSummary_none() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.showAllVisualEffects()
.allowAlarms(true)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
.isEqualTo("Notifications shown");
}
@Test
public void getBlockedEffectsSummary_some() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.showAllVisualEffects()
.showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
.isEqualTo("Notifications partially hidden");
}
@Test
public void getBlockedEffectsSummary_all() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowAlarms(true)
.hideAllVisualEffects()
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
.isEqualTo("Notifications hidden");
}
@Test
public void getDisplayEffectsSummary_single_notifVis() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.showAllVisualEffects()
.showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications partially hidden");
}
@Test
public void getDisplayEffectsSummary_single_notifVis_unusedEffect() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.showAllVisualEffects()
.showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications shown");
}
@Test
public void getDisplayEffectsSummary_single_displayEffect() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Dim the wallpaper");
}
@Test
public void getDisplayEffectsSummary_duo() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.setShouldDisplayGrayscale(true)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Grayscale and dim the wallpaper");
}
@Test
public void getDisplayEffectsSummary_trio() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.hideAllVisualEffects()
.allowAlarms(true)
.allowMedia(true)
.allowSystem(true)
.build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDisplayGrayscale(true)
.setShouldDimWallpaper(true)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications hidden, grayscale, and dim the wallpaper");
}
@Test
public void getDisplayEffectsSummary_quad() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.showAllVisualEffects()
.showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
.allowAlarms(true)
.allowMedia(true)
.allowSystem(true)
.build())
.setDeviceEffects(new ZenDeviceEffects.Builder()
.setShouldDimWallpaper(true)
.setShouldDisplayGrayscale(true)
.setShouldUseNightMode(true)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications partially hidden, grayscale, and 2 more");
}
} }