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

@@ -90,7 +90,7 @@ class ZenMode {
.build();
private final String mId;
private final AutomaticZenRule mRule;
private AutomaticZenRule mRule;
private final boolean mIsActive;
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
* 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
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<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend));
prefControllers.add(new ZenModeOtherLinkPreferenceController(
context, "zen_other_settings", mBackend));
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
context, "mode_display_settings", mBackend));
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_REPEAT_CALLERS;
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.icu.text.MessageFormat;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import android.util.SparseArray;
import com.android.settings.R;
import java.util.ArrayList;
@@ -129,10 +139,22 @@ public class ZenModeSummaryHelper {
}
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(
R.string.zen_mode_restrict_notifications_summary_muted);
} else if (zenMode.getPolicy().shouldHideAllVisualEffects()) {
} else if (shouldHideAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_hidden);
} 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,
Predicate<Integer> filteredCategories, boolean capitalizeFirstInList) {
List<String> enabledCategories = new ArrayList<>();