Merge "Redesign zen visual effects screens"

This commit is contained in:
Julia Reynolds
2018-04-27 11:08:57 +00:00
committed by Android (Google) Code Review
20 changed files with 1507 additions and 60 deletions

View File

@@ -54,6 +54,7 @@ abstract public class AbstractZenModePreferenceController extends
protected static ZenModeConfigWrapper mZenModeConfigWrapper;
protected MetricsFeatureProvider mMetricsFeatureProvider;
protected final ZenModeBackend mBackend;
protected PreferenceScreen mScreen;
public AbstractZenModePreferenceController(Context context, String key,
Lifecycle lifecycle) {
@@ -71,16 +72,26 @@ abstract public class AbstractZenModePreferenceController extends
mBackend = ZenModeBackend.getInstance(context);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mSettingObserver = new SettingObserver(screen.findPreference(KEY));
mScreen = screen;
Preference pref = screen.findPreference(KEY);
if (pref != null) {
mSettingObserver = new SettingObserver(pref);
}
}
@Override
public void onResume() {
if (mSettingObserver != null) {
mSettingObserver.register(mContext.getContentResolver());
mSettingObserver.onChange(false, null);
}
}
@@ -91,14 +102,6 @@ abstract public class AbstractZenModePreferenceController extends
}
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mBackend.updatePolicy();
mBackend.updateZenMode();
}
protected NotificationManager.Policy getPolicy() {
return mNotificationManager.getNotificationPolicy();
}
@@ -144,8 +147,13 @@ abstract public class AbstractZenModePreferenceController extends
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (ZEN_MODE_URI.equals(uri) || ZEN_MODE_CONFIG_ETAG_URI.equals(uri)
if (uri == null || ZEN_MODE_URI.equals(uri) || ZEN_MODE_CONFIG_ETAG_URI.equals(uri)
|| ZEN_MODE_DURATION_URI.equals(uri)) {
mBackend.updatePolicy();
mBackend.updateZenMode();
if (mScreen != null) {
displayPreference(mScreen);
}
updateState(mPreference);
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.content.Context;
import androidx.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RadioButton;
import com.android.settings.R;
import com.android.settingslib.TwoTargetPreference;
/**
* A radio button preference with a divider and a settings icon that links to another screen.
*/
public class ZenCustomRadioButtonPreference extends TwoTargetPreference
implements View.OnClickListener {
private RadioButton mButton;
private boolean mChecked;
private OnGearClickListener mOnGearClickListener;
private OnRadioButtonClickListener mOnRadioButtonClickListener;
public ZenCustomRadioButtonPreference(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(R.layout.preference_two_target_radio);
}
public ZenCustomRadioButtonPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setLayoutResource(R.layout.preference_two_target_radio);
}
public ZenCustomRadioButtonPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setLayoutResource(R.layout.preference_two_target_radio);
}
public ZenCustomRadioButtonPreference(Context context) {
super(context);
setLayoutResource(R.layout.preference_two_target_radio);
}
@Override
protected int getSecondTargetResId() {
return R.layout.preference_widget_gear;
}
public void setOnGearClickListener(OnGearClickListener l) {
mOnGearClickListener = l;
notifyChanged();
}
public void setOnRadioButtonClickListener(OnRadioButtonClickListener l) {
mOnRadioButtonClickListener = l;
notifyChanged();
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
View buttonFrame = holder.findViewById(R.id.checkbox_frame);
if (buttonFrame != null) {
buttonFrame.setOnClickListener(this);
}
mButton = (RadioButton) holder.findViewById(android.R.id.checkbox);
if (mButton != null) {
mButton.setChecked(mChecked);
}
final View gear = holder.findViewById(android.R.id.widget_frame);
if (mOnGearClickListener != null) {
gear.setVisibility(View.VISIBLE);
gear.setOnClickListener(this);
} else {
gear.setVisibility(View.GONE);
gear.setOnClickListener(null);
}
}
public boolean isChecked() {
return mButton != null && mChecked;
}
public void setChecked(boolean checked) {
mChecked = checked;
if (mButton != null) {
mButton.setChecked(checked);
}
}
public RadioButton getRadioButton() {
return mButton;
}
@Override
public void onClick() {
if (mOnRadioButtonClickListener != null) {
mOnRadioButtonClickListener.onRadioButtonClick(this);
}
}
@Override
public void onClick(View v) {
if (v.getId() == android.R.id.widget_frame) {
if (mOnGearClickListener != null) {
mOnGearClickListener.onGearClick(this);
}
} else if (v.getId() == R.id.checkbox_frame) {
if (mOnRadioButtonClickListener != null) {
mOnRadioButtonClickListener.onRadioButtonClick(this);
}
}
}
public interface OnGearClickListener {
void onGearClick(ZenCustomRadioButtonPreference p);
}
public interface OnRadioButtonClickListener {
void onRadioButtonClick(ZenCustomRadioButtonPreference p);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.app.NotificationManager.Policy;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenFooterPreferenceController extends AbstractZenModePreferenceController {
public ZenFooterPreferenceController(Context context, Lifecycle lifecycle,
String key) {
super(context, key, lifecycle);
}
@Override
public boolean isAvailable() {
return mBackend.mPolicy.suppressedVisualEffects == 0
|| Policy.areAllVisualEffectsSuppressed(mBackend.mPolicy.suppressedVisualEffects);
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
if (mBackend.mPolicy.suppressedVisualEffects == 0) {
preference.setTitle(R.string.zen_mode_restrict_notifications_mute_footer);
} else if (Policy.areAllVisualEffectsSuppressed(mBackend.mPolicy.suppressedVisualEffects)) {
preference.setTitle(R.string.zen_mode_restrict_notifications_hide_footer);
} else {
preference.setTitle(null);
}
}
protected void hide(PreferenceScreen screen) {
setVisible(screen, getPreferenceKey(), false /* visible */);
}
}

View File

@@ -0,0 +1,151 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_ZEN_SHOW_CUSTOM;
import android.content.Context;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
import java.util.List;
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class ZenModeRestrictNotificationsSettings extends ZenModeSettingsBase implements Indexable {
protected static final int APP_MENU_SHOW_CUSTOM = 1;
protected boolean mShowMenuSelected;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, APP_MENU_SHOW_CUSTOM, 0, R.string.zen_mode_restrict_notifications_enable_custom)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getItemId() == APP_MENU_SHOW_CUSTOM) {
final FeatureFactory featureFactory = FeatureFactory.getFactory(mContext);
MetricsFeatureProvider metrics = featureFactory.getMetricsFeatureProvider();
mShowMenuSelected = !mShowMenuSelected;
ZenModeVisEffectsCustomPreferenceController custom =
use(ZenModeVisEffectsCustomPreferenceController.class);
custom.setShownByMenu(mShowMenuSelected);
if (mShowMenuSelected) {
custom.select();
metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, true);
} else {
metrics.action(mContext, ACTION_ZEN_SHOW_CUSTOM, false);
}
return true;
}
return false;
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
if (mShowMenuSelected && !use(ZenModeVisEffectsCustomPreferenceController.class)
.areCustomOptionsSelected()) {
menu.findItem(APP_MENU_SHOW_CUSTOM)
.setTitle(R.string.zen_mode_restrict_notifications_disable_custom);
} else {
menu.findItem(APP_MENU_SHOW_CUSTOM)
.setTitle(R.string.zen_mode_restrict_notifications_enable_custom);
}
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getLifecycle());
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) {
List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeVisEffectsNonePreferenceController(
context, lifecycle, "zen_mute_notifications"));
controllers.add(new ZenModeVisEffectsAllPreferenceController(
context, lifecycle, "zen_hide_notifications"));
controllers.add(new ZenModeVisEffectsCustomPreferenceController(
context, lifecycle, "zen_custom"));
controllers.add(new ZenFooterPreferenceController(context, lifecycle,
FooterPreference.KEY_FOOTER));
return controllers;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.zen_mode_restrict_notifications_settings;
}
@Override
public int getMetricsCategory() {
return MetricsEvent.SETTINGS_ZEN_NOTIFICATIONS;
}
/**
* For Search.
*/
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final ArrayList<SearchIndexableResource> result = new ArrayList<>();
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.zen_mode_restrict_notifications_settings;
result.add(sir);
return result;
}
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
return keys;
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, null);
}
};
}

View File

@@ -160,24 +160,15 @@ public class ZenModeSettings extends ZenModeSettingsBase {
}
String getBlockedEffectsSummary(Policy policy) {
List<String> blockedStrings = new ArrayList<>();
if (Policy.areAnyScreenOffEffectsSuppressed(policy.suppressedVisualEffects)) {
blockedStrings.add(mContext.getResources().getString(
R.string.zen_mode_block_effect_summary_screen_off));
}
if (Policy.areAnyScreenOnEffectsSuppressed(policy.suppressedVisualEffects)) {
blockedStrings.add(mContext.getResources().getString(
R.string.zen_mode_block_effect_summary_screen_on));
}
if (blockedStrings.size() == 0) {
if (policy.suppressedVisualEffects == 0) {
return mContext.getResources().getString(
R.string.zen_mode_block_effect_summary_none);
} else if (blockedStrings.size() == 1) {
return blockedStrings.get(0);
R.string.zen_mode_restrict_notifications_summary_muted);
} else if (Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)) {
return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_hidden);
} else {
return mContext.getResources().getString(R.string.join_two_unrelated_items,
blockedStrings.get(0), blockedStrings.get(1));
return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_custom);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.app.NotificationManager.Policy;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeVisEffectsAllPreferenceController
extends AbstractZenModePreferenceController
implements ZenCustomRadioButtonPreference.OnRadioButtonClickListener {
protected static final int EFFECTS = Policy.SUPPRESSED_EFFECT_SCREEN_OFF
| Policy.SUPPRESSED_EFFECT_SCREEN_ON
| Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| Policy.SUPPRESSED_EFFECT_LIGHTS
| Policy.SUPPRESSED_EFFECT_PEEK
| Policy.SUPPRESSED_EFFECT_STATUS_BAR
| Policy.SUPPRESSED_EFFECT_BADGE
| Policy.SUPPRESSED_EFFECT_AMBIENT
| Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
public ZenModeVisEffectsAllPreferenceController(Context context, Lifecycle lifecycle,
String key) {
super(context, key, lifecycle);
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
boolean everythingBlocked = Policy.areAllVisualEffectsSuppressed(
mBackend.mPolicy.suppressedVisualEffects);
ZenCustomRadioButtonPreference pref = (ZenCustomRadioButtonPreference) preference;
pref.setOnRadioButtonClickListener(this);
pref.setChecked(everythingBlocked);
}
protected void deselect(PreferenceScreen screen) {
ZenCustomRadioButtonPreference preference =
(ZenCustomRadioButtonPreference) screen.findPreference(getPreferenceKey());
if (preference != null) {
preference.setChecked(false);
}
}
@Override
public void onRadioButtonClick(ZenCustomRadioButtonPreference p) {
mMetricsFeatureProvider.action(mContext,
MetricsProto.MetricsEvent.ACTION_ZEN_SOUND_AND_VIS_EFFECTS, true);
mBackend.saveVisualEffectsPolicy(EFFECTS, true);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.app.NotificationManager.Policy;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeVisEffectsCustomPreferenceController
extends AbstractZenModePreferenceController {
protected boolean mShowMenuSelected;
protected static final int INTERRUPTIVE_EFFECTS = Policy.SUPPRESSED_EFFECT_AMBIENT
| Policy.SUPPRESSED_EFFECT_PEEK
| Policy.SUPPRESSED_EFFECT_LIGHTS
| Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
public ZenModeVisEffectsCustomPreferenceController(Context context, Lifecycle lifecycle,
String key) {
super(context, key, lifecycle);
}
@Override
public boolean isAvailable() {
if (mShowMenuSelected) {
return true;
}
return areCustomOptionsSelected();
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
ZenCustomRadioButtonPreference pref = (ZenCustomRadioButtonPreference) preference;
pref.setChecked(areCustomOptionsSelected());
pref.setOnGearClickListener(p -> {
new SubSettingLauncher(mContext)
.setDestination(ZenModeBlockedEffectsSettings.class.getName())
.setTitle(R.string.zen_mode_what_to_block_title)
.setSourceMetricsCategory(MetricsProto.MetricsEvent.SETTINGS_ZEN_NOTIFICATIONS)
.launch();
});
pref.setOnRadioButtonClickListener(p -> {
select();
});
}
protected void setShownByMenu(boolean shown) {
mShowMenuSelected = shown;
}
protected boolean areCustomOptionsSelected() {
boolean allEffectsSuppressed =
Policy.areAllVisualEffectsSuppressed(mBackend.mPolicy.suppressedVisualEffects);
boolean noEffectsSuppressed = mBackend.mPolicy.suppressedVisualEffects == 0;
return !(allEffectsSuppressed || noEffectsSuppressed);
}
protected void select() {
mMetricsFeatureProvider.action(mContext,
MetricsProto.MetricsEvent.ACTION_ZEN_CUSTOM, true);
mBackend.savePolicy(mBackend.mPolicy.priorityCategories,
mBackend.mPolicy.priorityCallSenders,
mBackend.mPolicy.priorityMessageSenders,
INTERRUPTIVE_EFFECTS);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification;
import android.app.NotificationManager.Policy;
import android.content.Context;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeVisEffectsNonePreferenceController
extends AbstractZenModePreferenceController
implements ZenCustomRadioButtonPreference.OnRadioButtonClickListener {
protected static final int EFFECTS = Policy.SUPPRESSED_EFFECT_SCREEN_OFF
| Policy.SUPPRESSED_EFFECT_SCREEN_ON
| Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT
| Policy.SUPPRESSED_EFFECT_LIGHTS
| Policy.SUPPRESSED_EFFECT_PEEK
| Policy.SUPPRESSED_EFFECT_STATUS_BAR
| Policy.SUPPRESSED_EFFECT_BADGE
| Policy.SUPPRESSED_EFFECT_AMBIENT
| Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
public ZenModeVisEffectsNonePreferenceController(Context context, Lifecycle lifecycle,
String key) {
super(context, key, lifecycle);
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
boolean nothingBlocked = mBackend.mPolicy.suppressedVisualEffects == 0;
ZenCustomRadioButtonPreference pref = (ZenCustomRadioButtonPreference) preference;
pref.setOnRadioButtonClickListener(this);
pref.setChecked(nothingBlocked);
}
@Override
public void onRadioButtonClick(ZenCustomRadioButtonPreference preference) {
mMetricsFeatureProvider.action(mContext,
MetricsProto.MetricsEvent.ACTION_ZEN_SOUND_ONLY, true);
mBackend.saveVisualEffectsPolicy(EFFECTS, false);
}
protected void deselect(PreferenceScreen screen) {
ZenCustomRadioButtonPreference preference =
(ZenCustomRadioButtonPreference) screen.findPreference(getPreferenceKey());
if (preference != null) {
preference.setChecked(false);
}
}
}