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:
@@ -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>
|
||||||
|
45
res/xml/modes_display_settings.xml
Normal file
45
res/xml/modes_display_settings.xml
Normal 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>
|
59
res/xml/modes_notif_vis_settings.xml
Normal file
59
res/xml/modes_notif_vis_settings.xml
Normal 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>
|
@@ -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>
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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<>();
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user