diff --git a/res/values/strings.xml b/res/values/strings.xml index c38c86bcbca..7331d7265f7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6741,6 +6741,9 @@ Automatic rules + + Automatic rule + Set Do Not Disturb rules diff --git a/res/xml/zen_mode_event_rule_settings.xml b/res/xml/zen_mode_event_rule_settings.xml index 102d2a2e974..159dbe0e362 100644 --- a/res/xml/zen_mode_event_rule_settings.xml +++ b/res/xml/zen_mode_event_rule_settings.xml @@ -15,8 +15,18 @@ limitations under the License. --> - + + + + + - - - diff --git a/res/xml/zen_mode_schedule_rule_settings.xml b/res/xml/zen_mode_schedule_rule_settings.xml index 6224ce11a93..a0c52c09728 100644 --- a/res/xml/zen_mode_schedule_rule_settings.xml +++ b/res/xml/zen_mode_schedule_rule_settings.xml @@ -15,8 +15,18 @@ limitations under the License. --> - + + + + + - - - diff --git a/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java index ec9cf2ab59d..cc70a6f549e 100644 --- a/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java +++ b/src/com/android/settings/notification/AbstractZenModeAutomaticRulePreferenceController.java @@ -29,8 +29,9 @@ import android.service.notification.ConditionProviderService; import android.service.notification.ZenModeConfig; import android.support.v7.preference.Preference; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.Arrays; import java.util.Comparator; @@ -38,19 +39,19 @@ import java.util.Map; import java.util.Set; abstract public class AbstractZenModeAutomaticRulePreferenceController extends - AbstractPreferenceController implements PreferenceControllerMixin { + AbstractZenModePreferenceController implements PreferenceControllerMixin { - private static final String TAG = "ZenModeAutomaticRule"; protected ZenModeBackend mBackend; protected Fragment mParent; protected Set> mRules; protected PackageManager mPm; - public AbstractZenModeAutomaticRulePreferenceController(Context context, Fragment parent) { - super(context); + public AbstractZenModeAutomaticRulePreferenceController(Context context, String key, Fragment + parent, Lifecycle lifecycle) { + super(context, key, lifecycle); mBackend = ZenModeBackend.getInstance(context); - mParent = parent; mPm = mContext.getPackageManager(); + mParent = parent; } @Override @@ -65,19 +66,9 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends return ruleMap.entrySet(); } - protected void showNameRuleDialog(final ZenRuleInfo ri) { - new ZenRuleNameDialog(mContext, null, ri.defaultConditionId) { - @Override - public void onOk(String ruleName) { - AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent, - ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY, - true); - String savedRuleId = mBackend.addZenRule(rule); - if (savedRuleId != null) { - mParent.startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId)); - } - } - }.show(); + protected void showNameRuleDialog(final ZenRuleInfo ri, Fragment parent) { + ZenRuleNameDialog.show(parent, null, ri.defaultConditionId, new + RuleNameChangeListener(ri)); } protected Map.Entry[] sortedRules() { @@ -157,4 +148,26 @@ abstract public class AbstractZenModeAutomaticRulePreferenceController extends } return null; } + + public class RuleNameChangeListener implements ZenRuleNameDialog.PositiveClickListener { + ZenRuleInfo mRuleInfo; + + public RuleNameChangeListener(ZenRuleInfo ruleInfo) { + mRuleInfo = ruleInfo; + } + + @Override + public void onOk(String ruleName, Fragment parent) { + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK); + AutomaticZenRule rule = new AutomaticZenRule(ruleName, mRuleInfo.serviceComponent, + mRuleInfo.defaultConditionId, + NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); + String savedRuleId = mBackend.addZenRule(rule); + if (savedRuleId != null) { + parent.startActivity(getRuleIntent(mRuleInfo.settingsAction, null, + savedRuleId)); + } + } + } } diff --git a/src/com/android/settings/notification/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/AbstractZenModePreferenceController.java index 2642f8129e9..33c027cd789 100644 --- a/src/com/android/settings/notification/AbstractZenModePreferenceController.java +++ b/src/com/android/settings/notification/AbstractZenModePreferenceController.java @@ -35,6 +35,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -51,6 +53,7 @@ abstract public class AbstractZenModePreferenceController extends private final String KEY; final private NotificationManager mNotificationManager; protected static ZenModeConfigWrapper mZenModeConfigWrapper; + protected MetricsFeatureProvider mMetricsFeatureProvider; public AbstractZenModePreferenceController(Context context, String key, Lifecycle lifecycle) { @@ -62,6 +65,9 @@ abstract public class AbstractZenModePreferenceController extends KEY = key; mNotificationManager = (NotificationManager) context.getSystemService( Context.NOTIFICATION_SERVICE); + + final FeatureFactory featureFactory = FeatureFactory.getFactory(mContext); + mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider(); } @Override diff --git a/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java new file mode 100644 index 00000000000..84949988bed --- /dev/null +++ b/src/com/android/settings/notification/ZenAutomaticRuleHeaderPreferenceController.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 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.settings.widget.EntityHeaderController.PREF_KEY_APP_HEADER; + +import android.app.AutomaticZenRule; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.support.v14.preference.PreferenceFragment; +import android.support.v7.preference.Preference; +import android.util.Slog; +import android.view.View; + +import com.android.settings.R; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.widget.EntityHeaderController; +import com.android.settingslib.core.lifecycle.Lifecycle; + +public class ZenAutomaticRuleHeaderPreferenceController extends AbstractZenModePreferenceController + implements PreferenceControllerMixin { + + private final String KEY = PREF_KEY_APP_HEADER; + private final PreferenceFragment mFragment; + private AutomaticZenRule mRule; + private EntityHeaderController mController; + + public ZenAutomaticRuleHeaderPreferenceController(Context context, PreferenceFragment fragment, + Lifecycle lifecycle) { + super(context, PREF_KEY_APP_HEADER, lifecycle); + mFragment = fragment; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public boolean isAvailable() { + return mRule != null; + } + + public void updateState(Preference preference) { + if (mRule == null) { + return; + } + + if (mFragment != null) { + LayoutPreference pref = (LayoutPreference) preference; + + if (mController == null) { + mController = EntityHeaderController + .newInstance(mFragment.getActivity(), mFragment, + pref.findViewById(R.id.entity_header)); + } + + pref = mController.setIcon(getIcon()) + .setLabel(mRule.getName()) + .setPackageName(mRule.getOwner().getPackageName()) + .setUid(mContext.getUserId()) + .setHasAppInfoLink(false) + .setButtonActions(EntityHeaderController.ActionType.ACTION_NONE, + EntityHeaderController.ActionType.ACTION_NONE) + .done(mFragment.getActivity(), mContext); + + pref.findViewById(R.id.entity_header).setVisibility(View.VISIBLE); + } + } + + private Drawable getIcon() { + try { + PackageManager packageManager = mContext.getPackageManager(); + ApplicationInfo info = packageManager.getApplicationInfo( + mRule.getOwner().getPackageName(), 0); + return info.loadIcon(packageManager); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Unable to load icon - PackageManager.NameNotFoundException"); + } + + return null; + } + + protected void onResume(AutomaticZenRule rule) { + mRule = rule; + } +} diff --git a/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java b/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java new file mode 100644 index 00000000000..bc3fa2596ba --- /dev/null +++ b/src/com/android/settings/notification/ZenAutomaticRuleSwitchPreferenceController.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 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.AutomaticZenRule; +import android.app.Fragment; +import android.content.Context; +import android.support.v7.preference.Preference; +import android.widget.Switch; +import android.widget.Toast; + +import com.android.settings.R; +import com.android.settings.applications.LayoutPreference; +import com.android.settings.widget.SwitchBar; +import com.android.settingslib.core.lifecycle.Lifecycle; + +public class ZenAutomaticRuleSwitchPreferenceController extends + AbstractZenModeAutomaticRulePreferenceController implements + SwitchBar.OnSwitchChangeListener { + + private static final String KEY = "zen_automatic_rule_switch"; + private AutomaticZenRule mRule; + private String mId; + private Toast mEnabledToast; + private int mToastTextResource; + + public ZenAutomaticRuleSwitchPreferenceController(Context context, Fragment parent, + int toastTextResource, Lifecycle lifecycle) { + super(context, KEY, parent, lifecycle); + mToastTextResource = toastTextResource; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public boolean isAvailable() { + return mRule != null && mId != null; + } + + public void onResume(AutomaticZenRule rule, String id) { + mRule = rule; + mId = id; + } + + public void updateState(Preference preference) { + LayoutPreference pref = (LayoutPreference) preference; + SwitchBar bar = pref.findViewById(R.id.switch_bar); + if (mRule != null) { + bar.setChecked(mRule.isEnabled()); + } + if (bar != null) { + bar.show(); + try { + bar.addOnSwitchChangeListener(this); + } catch (IllegalStateException e) { + // an exception is thrown if you try to add the listener twice + } + } + bar.show(); + } + + @Override + public void onSwitchChanged(Switch switchView, boolean isChecked) { + final boolean enabled = isChecked; + if (enabled == mRule.isEnabled()) return; + mRule.setEnabled(enabled); + mBackend.setZenRule(mId, mRule); + if (enabled) { + final int toastText = mToastTextResource; + if (toastText != 0) { + mEnabledToast = Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT); + mEnabledToast.show(); + } + } else { + if (mEnabledToast != null) { + mEnabledToast.cancel(); + } + } + } +} diff --git a/src/com/android/settings/notification/ZenDeleteRuleDialog.java b/src/com/android/settings/notification/ZenDeleteRuleDialog.java new file mode 100644 index 00000000000..d9061d3b0a4 --- /dev/null +++ b/src/com/android/settings/notification/ZenDeleteRuleDialog.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 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.AlertDialog; +import android.app.Dialog; +import android.app.Fragment; +import android.content.DialogInterface; +import android.os.Bundle; +import android.view.View; + +import com.android.internal.logging.nano.MetricsProto; +import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; + +public class ZenDeleteRuleDialog extends InstrumentedDialogFragment { + protected static final String TAG = "ZenDeleteRuleDialog"; + private static final String EXTRA_ZEN_RULE_NAME = "zen_rule_name"; + private static final String EXTRA_ZEN_RULE_ID = "zen_rule_id"; + protected static PositiveClickListener mPositiveClickListener; + + /** + * The interface we expect a listener to implement. + */ + public interface PositiveClickListener { + void onOk(String id); + } + + public static void show(Fragment parent, String ruleName, String id, PositiveClickListener + listener) { + final Bundle args = new Bundle(); + args.putString(EXTRA_ZEN_RULE_NAME, ruleName); + args.putString(EXTRA_ZEN_RULE_ID, id); + mPositiveClickListener = listener; + + ZenDeleteRuleDialog dialog = new ZenDeleteRuleDialog(); + dialog.setArguments(args); + dialog.setTargetFragment(parent, 0); + dialog.show(parent.getFragmentManager(), TAG); + } + + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_DELETE_RULE_DIALOG; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle arguments = getArguments(); + String ruleName = arguments.getString(EXTRA_ZEN_RULE_NAME); + String id = arguments.getString(EXTRA_ZEN_RULE_ID); + + final AlertDialog dialog = new AlertDialog.Builder(getContext()) + .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName)) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.zen_mode_delete_rule_button, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (arguments != null) { + mPositiveClickListener.onOk(id); + } + } + }).create(); + final View messageView = dialog.findViewById(android.R.id.message); + if (messageView != null) { + messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE); + } + return dialog; + } + +} diff --git a/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java index a15536c12e7..b2e69d80d3f 100644 --- a/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java +++ b/src/com/android/settings/notification/ZenModeAddAutomaticRulePreferenceController.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceScreen; +import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settings.utils.ZenServiceListing; @@ -28,19 +29,18 @@ public class ZenModeAddAutomaticRulePreferenceController extends AbstractZenModeAutomaticRulePreferenceController implements Preference.OnPreferenceClickListener { - private final String KEY_ADD_RULE; + protected static final String KEY = "zen_mode_add_automatic_rule"; private final ZenServiceListing mZenServiceListing; - public ZenModeAddAutomaticRulePreferenceController(Context context, String key, - Fragment parent, ZenServiceListing serviceListing) { - super(context, parent); - KEY_ADD_RULE = key; + public ZenModeAddAutomaticRulePreferenceController(Context context, Fragment parent, + ZenServiceListing serviceListing, Lifecycle lifecycle) { + super(context, KEY, parent, lifecycle); mZenServiceListing = serviceListing; } @Override public String getPreferenceKey() { - return KEY_ADD_RULE; + return KEY; } @Override @@ -51,25 +51,30 @@ public class ZenModeAddAutomaticRulePreferenceController extends @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - Preference pref = screen.findPreference(KEY_ADD_RULE); + Preference pref = screen.findPreference(KEY); pref.setPersistent(false); pref.setOnPreferenceClickListener(this); } @Override public boolean onPreferenceClick(Preference preference) { - new ZenRuleSelectionDialog(mContext, mZenServiceListing) { - @Override - public void onSystemRuleSelected(ZenRuleInfo ri) { - showNameRuleDialog(ri); - } - - @Override - public void onExternalRuleSelected(ZenRuleInfo ri) { - Intent intent = new Intent().setComponent(ri.configurationActivity); - mParent.startActivity(intent); - } - }.show(); + ZenRuleSelectionDialog.show(mContext, mParent, new RuleSelectionListener(), + mZenServiceListing); return true; } + + public class RuleSelectionListener implements ZenRuleSelectionDialog.PositiveClickListener { + public RuleSelectionListener() {} + + @Override + public void onSystemRuleSelected(ZenRuleInfo ri, Fragment parent) { + showNameRuleDialog(ri, parent); + } + + @Override + public void onExternalRuleSelected(ZenRuleInfo ri, Fragment parent) { + Intent intent = new Intent().setComponent(ri.configurationActivity); + parent.startActivity(intent); + } + } } diff --git a/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java index ef8d0263da1..a15f7fca550 100644 --- a/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeAlarmsPreferenceController.java @@ -23,6 +23,7 @@ import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeAlarmsPreferenceController extends @@ -73,6 +74,9 @@ public class ZenModeAlarmsPreferenceController extends if (ZenModeSettingsBase.DEBUG) { Log.d(TAG, "onPrefChange allowAlarms=" + allowAlarms); } + + mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_ALARMS, + allowAlarms); mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_ALARMS, allowAlarms); return true; } diff --git a/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java index f91bdd64f26..55fe9277e3b 100644 --- a/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceController.java @@ -19,28 +19,31 @@ package com.android.settings.notification; import android.app.AutomaticZenRule; import android.app.Fragment; import android.content.Context; +import android.support.annotation.VisibleForTesting; import android.support.v7.preference.Preference; import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceScreen; + +import com.android.settingslib.core.lifecycle.Lifecycle; + import java.util.Map; public class ZenModeAutomaticRulesPreferenceController extends AbstractZenModeAutomaticRulePreferenceController { - private final String KEY_AUTOMATIC_RULES; - private PreferenceCategory mPreferenceCategory; - Map.Entry[] mSortedRules; + protected static final String KEY = "zen_mode_automatic_rules"; - public ZenModeAutomaticRulesPreferenceController(Context context, String key, - Fragment parent) { - super(context, parent); - KEY_AUTOMATIC_RULES = key; - mSortedRules = sortedRules(); + @VisibleForTesting + protected PreferenceCategory mPreferenceCategory; + + public ZenModeAutomaticRulesPreferenceController(Context context, Fragment parent, Lifecycle + lifecycle) { + super(context, KEY, parent, lifecycle); } @Override public String getPreferenceKey() { - return KEY_AUTOMATIC_RULES; + return KEY; } @Override @@ -59,40 +62,14 @@ public class ZenModeAutomaticRulesPreferenceController extends public void updateState(Preference preference) { super.updateState(preference); - // no need to update AutomaticRule if a rule was deleted - // (on rule deletion, the preference removes itself from its parent) - int oldRuleLength = mSortedRules.length; - mSortedRules = sortedRules(); - if (!wasRuleDeleted(oldRuleLength)) { - updateAutomaticRules(); + mPreferenceCategory.removeAll(); + Map.Entry[] sortedRules = sortedRules(); + for (Map.Entry sortedRule : sortedRules) { + ZenRulePreference pref = new ZenRulePreference(mPreferenceCategory.getContext(), + sortedRule, mParent, mMetricsFeatureProvider); + mPreferenceCategory.addPreference(pref); } } - - private boolean wasRuleDeleted(int oldRuleLength) { - int newRuleLength = mSortedRules.length; - int prefCount = mPreferenceCategory.getPreferenceCount(); - - return (prefCount == oldRuleLength -1) && (prefCount == newRuleLength); - } - - private void updateAutomaticRules() { - for (Map.Entry sortedRule : mSortedRules) { - ZenRulePreference currPref = (ZenRulePreference) - mPreferenceCategory.findPreference(sortedRule.getKey()); - if (currPref != null && currPref.appExists) { - // rule already exists in preferences, update it - currPref.setAttributes(sortedRule.getValue()); - } else { - // rule doesn't exist in preferences, add it - ZenRulePreference pref = new ZenRulePreference(mPreferenceCategory.getContext(), - sortedRule, mPreferenceCategory); - if (pref.appExists) { - mPreferenceCategory.addPreference(pref); - } - } - } - - } } diff --git a/src/com/android/settings/notification/ZenModeAutomationSettings.java b/src/com/android/settings/notification/ZenModeAutomationSettings.java index 582fb03b470..55d0fca8efd 100644 --- a/src/com/android/settings/notification/ZenModeAutomationSettings.java +++ b/src/com/android/settings/notification/ZenModeAutomationSettings.java @@ -28,29 +28,27 @@ import com.android.settings.search.Indexable; import com.android.settings.utils.ManagedServiceSettings; import com.android.settings.utils.ZenServiceListing; import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.ArrayList; import java.util.List; public class ZenModeAutomationSettings extends ZenModeSettingsBase { - private static final String KEY_ADD_RULE = "zen_mode_add_automatic_rule"; - private static final String KEY_AUTOMATIC_RULES = "zen_mode_automatic_rules"; - protected static final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig(); + protected final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig(); @Override protected List getPreferenceControllers(Context context) { ZenServiceListing serviceListing = new ZenServiceListing(getContext(), CONFIG); serviceListing.reloadApprovedServices(); - return buildPreferenceControllers(context, this, serviceListing); + return buildPreferenceControllers(context, this, serviceListing, getLifecycle()); } private static List buildPreferenceControllers(Context context, - Fragment parent, ZenServiceListing serviceListing) { + Fragment parent, ZenServiceListing serviceListing, Lifecycle lifecycle) { List controllers = new ArrayList<>(); - controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, KEY_ADD_RULE, - parent, serviceListing)); - controllers.add(new ZenModeAutomaticRulesPreferenceController(context, - KEY_AUTOMATIC_RULES, parent)); + controllers.add(new ZenModeAddAutomaticRulePreferenceController(context, parent, + serviceListing, lifecycle)); + controllers.add(new ZenModeAutomaticRulesPreferenceController(context, parent, lifecycle)); return controllers; } @@ -94,15 +92,15 @@ public class ZenModeAutomationSettings extends ZenModeSettingsBase { @Override public List getNonIndexableKeys(Context context) { final List keys = super.getNonIndexableKeys(context); - keys.add(KEY_ADD_RULE); - keys.add(KEY_AUTOMATIC_RULES); + keys.add(ZenModeAddAutomaticRulePreferenceController.KEY); + keys.add(ZenModeAutomaticRulesPreferenceController.KEY); return keys; } @Override public List getPreferenceControllers( Context context) { - return buildPreferenceControllers(context, null, null); + return buildPreferenceControllers(context, null, null, null); } }; } diff --git a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java index 79115f2d044..1886dab1524 100644 --- a/src/com/android/settings/notification/ZenModeButtonPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeButtonPreferenceController.java @@ -22,6 +22,7 @@ import android.support.v7.preference.Preference; import android.view.View; import android.widget.Button; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; import com.android.settings.applications.LayoutPreference; import com.android.settings.core.PreferenceControllerMixin; @@ -57,15 +58,21 @@ public class ZenModeButtonPreferenceController extends AbstractZenModePreference if (null == mZenButtonOn) { mZenButtonOn = (Button) ((LayoutPreference) preference) .findViewById(R.id.zen_mode_settings_turn_on_button); - mZenButtonOn.setOnClickListener(v -> - mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)); + mZenButtonOn.setOnClickListener(v -> { + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, true); + mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); + }); } if (null == mZenButtonOff) { mZenButtonOff = (Button) ((LayoutPreference) preference) .findViewById(R.id.zen_mode_settings_turn_off_button); - mZenButtonOff.setOnClickListener(v -> - mBackend.setZenMode(Settings.Global.ZEN_MODE_OFF)); + mZenButtonOff.setOnClickListener(v -> { + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, false); + mBackend.setZenMode(Settings.Global.ZEN_MODE_OFF); + }); } updateButtons(); diff --git a/src/com/android/settings/notification/ZenModeEventRuleSettings.java b/src/com/android/settings/notification/ZenModeEventRuleSettings.java index aa2cc3f44a3..bb667685ed0 100644 --- a/src/com/android/settings/notification/ZenModeEventRuleSettings.java +++ b/src/com/android/settings/notification/ZenModeEventRuleSettings.java @@ -60,16 +60,6 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { return mEvent != null; } - @Override - protected String getZenModeDependency() { - return null; - } - - @Override - protected int getEnabledToastText() { - return R.string.zen_event_rule_enabled_toast; - } - @Override public void onResume() { super.onResume(); @@ -89,7 +79,14 @@ public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { @Override protected List getPreferenceControllers(Context context) { - return null; + List controllers = new ArrayList<>(); + mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, + getLifecycle()); + mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, + R.string.zen_event_rule_enabled_toast, getLifecycle()); + controllers.add(mHeader); + controllers.add(mSwitch); + return controllers; } private void reloadCalendar() { diff --git a/src/com/android/settings/notification/ZenModeEventsPreferenceController.java b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java index 3763fed10b7..be5e6d6e925 100644 --- a/src/com/android/settings/notification/ZenModeEventsPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeEventsPreferenceController.java @@ -24,6 +24,7 @@ import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeEventsPreferenceController extends AbstractZenModePreferenceController @@ -71,6 +72,8 @@ public class ZenModeEventsPreferenceController extends AbstractZenModePreference if (ZenModeSettingsBase.DEBUG) { Log.d(TAG, "onPrefChange allowEvents=" + allowEvents); } + mMetricsFeatureProvider.action(mContext, MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_EVENTS, + allowEvents); mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_EVENTS, allowEvents); return true; } diff --git a/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java index edc7cf90d5c..99a4f0d7b64 100644 --- a/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeRemindersPreferenceController.java @@ -23,6 +23,7 @@ import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeRemindersPreferenceController extends AbstractZenModePreferenceController @@ -67,7 +68,11 @@ public class ZenModeRemindersPreferenceController extends AbstractZenModePrefere @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean allowReminders = (Boolean) newValue; - if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowReminders=" + allowReminders); + if (ZenModeSettingsBase.DEBUG) { + Log.d(TAG, "onPrefChange allowReminders=" + allowReminders); + } + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_REMINDERS, allowReminders); mBackend.saveSoundPolicy(NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS, allowReminders); return true; diff --git a/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java index 1d184096a65..82fe865885c 100644 --- a/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeRepeatCallersPreferenceController.java @@ -23,6 +23,7 @@ import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController @@ -77,8 +78,11 @@ public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePre @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean allowRepeatCallers = (Boolean) newValue; - if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers=" - + allowRepeatCallers); + if (ZenModeSettingsBase.DEBUG) { + Log.d(TAG, "onPrefChange allowRepeatCallers=" + allowRepeatCallers); + } + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_REPEAT_CALLS, allowRepeatCallers); mBackend.saveSoundPolicy(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, allowRepeatCallers); return true; } diff --git a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java index 069d38b8dd9..0234c8e2ee3 100644 --- a/src/com/android/settings/notification/ZenModeRuleSettingsBase.java +++ b/src/com/android/settings/notification/ZenModeRuleSettingsBase.java @@ -16,62 +16,43 @@ package com.android.settings.notification; -import android.app.Activity; -import android.app.AlertDialog; import android.app.AutomaticZenRule; +import android.app.Fragment; import android.app.NotificationManager; import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.service.notification.ConditionProviderService; -import android.support.v7.preference.DropDownPreference; import android.support.v7.preference.Preference; -import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.Preference.OnPreferenceClickListener; import android.support.v7.preference.PreferenceScreen; import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.Switch; import android.widget.Toast; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.widget.SwitchBar; import com.android.settingslib.core.AbstractPreferenceController; -import java.util.List; +public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase { -public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase - implements SwitchBar.OnSwitchChangeListener { protected static final String TAG = ZenModeSettingsBase.TAG; protected static final boolean DEBUG = ZenModeSettingsBase.DEBUG; private static final String KEY_RULE_NAME = "rule_name"; - private static final String KEY_ZEN_MODE = "zen_mode"; protected Context mContext; protected boolean mDisableListeners; protected AutomaticZenRule mRule; protected String mId; - private boolean mDeleting; private Preference mRuleName; - private SwitchBar mSwitchBar; - private DropDownPreference mZenMode; - private Toast mEnabledToast; + protected ZenAutomaticRuleHeaderPreferenceController mHeader; + protected ZenAutomaticRuleSwitchPreferenceController mSwitch; abstract protected void onCreateInternal(); abstract protected boolean setRule(AutomaticZenRule rule); - abstract protected String getZenModeDependency(); abstract protected void updateControlsInternal(); - abstract protected int getEnabledToastText(); @Override public void onCreate(Bundle icicle) { @@ -99,8 +80,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase super.onCreate(icicle); - setHasOptionsMenu(true); - onCreateInternal(); final PreferenceScreen root = getPreferenceScreen(); @@ -112,37 +91,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase return true; } }); - - mZenMode = (DropDownPreference) root.findPreference(KEY_ZEN_MODE); - mZenMode.setEntries(new CharSequence[] { - getString(R.string.zen_mode_option_important_interruptions), - getString(R.string.zen_mode_option_alarms), - getString(R.string.zen_mode_option_no_interruptions), - }); - mZenMode.setEntryValues(new CharSequence[] { - Integer.toString(NotificationManager.INTERRUPTION_FILTER_PRIORITY), - Integer.toString(NotificationManager.INTERRUPTION_FILTER_ALARMS), - Integer.toString(NotificationManager.INTERRUPTION_FILTER_NONE), - }); - mZenMode.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (mDisableListeners) return false; - final int zenMode = Integer.parseInt((String) newValue); - if (zenMode == mRule.getInterruptionFilter()) return false; - if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode); - mRule.setInterruptionFilter(zenMode); - mBackend.setZenRule(mId, mRule); - return true; - } - }); - mZenMode.setOrder(10); // sort at the bottom of the category - mZenMode.setDependency(getZenModeDependency()); - } - - @Override - protected List getPreferenceControllers(Context context) { - return null; } @Override @@ -155,43 +103,39 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - final SettingsActivity activity = (SettingsActivity) getActivity(); - mSwitchBar = activity.getSwitchBar(); - mSwitchBar.addOnSwitchChangeListener(this); - mSwitchBar.show(); + public int getHelpResource() { + return R.string.help_uri_interruptions; } - @Override - public void onDestroyView() { - super.onDestroyView(); - mSwitchBar.removeOnSwitchChangeListener(this); - mSwitchBar.hide(); + /** + * Update state of header preference managed by PreferenceController. + */ + protected void updateHeader() { + final PreferenceScreen screen = getPreferenceScreen(); + + mSwitch.onResume(mRule,mId); + mSwitch.displayPreference(screen); + updatePreference(mSwitch); + + mHeader.onResume(mRule); + mHeader.displayPreference(screen); + updatePreference(mHeader); } - @Override - public void onSwitchChanged(Switch switchView, boolean isChecked) { - if (DEBUG) Log.d(TAG, "onSwitchChanged " + isChecked); - if (mDisableListeners) return; - final boolean enabled = isChecked; - if (enabled == mRule.isEnabled()) return; - mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ENABLE_RULE, enabled); - if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled); - mRule.setEnabled(enabled); - mBackend.setZenRule(mId, mRule); - if (enabled) { - final int toastText = getEnabledToastText(); - if (toastText != 0) { - mEnabledToast = Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT); - mEnabledToast.show(); - } - } else { - if (mEnabledToast != null) { - mEnabledToast.cancel(); - } + private void updatePreference(AbstractPreferenceController controller) { + final PreferenceScreen screen = getPreferenceScreen(); + if (!controller.isAvailable()) { + return; } + final String key = controller.getPreferenceKey(); + + final Preference preference = screen.findPreference(key); + if (preference == null) { + Log.d(TAG, String.format("Cannot find preference with key %s in Controller %s", + key, controller.getClass().getSimpleName())); + return; + } + controller.updateState(preference); } protected void updateRule(Uri newConditionId) { @@ -207,33 +151,6 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase } } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (DEBUG) Log.d(TAG, "onCreateOptionsMenu"); - inflater.inflate(R.menu.zen_mode_rule, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (DEBUG) Log.d(TAG, "onOptionsItemSelected " + item.getItemId()); - if (item.getItemId() == R.id.delete) { - mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_DELETE_RULE); - showDeleteRuleDialog(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void showRuleNameDialog() { - new ZenRuleNameDialog(mContext, mRule.getName(), null) { - @Override - public void onOk(String ruleName) { - mRule.setName(ruleName); - mBackend.setZenRule(mId, mRule); - } - }.show(); - } - private boolean refreshRuleOrFinish() { mRule = getZenRule(); if (DEBUG) Log.d(TAG, "mRule=" + mRule); @@ -244,42 +161,22 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase return false; } - private void showDeleteRuleDialog() { - final AlertDialog dialog = new AlertDialog.Builder(mContext) - .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, mRule.getName())) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.zen_mode_delete_rule_button, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mMetricsFeatureProvider.action(mContext, - MetricsEvent.ACTION_ZEN_DELETE_RULE_OK); - mDeleting = true; - mBackend.removeZenRule(mId); - } - }) - .show(); - final View messageView = dialog.findViewById(android.R.id.message); - if (messageView != null) { - messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE); - } + private void showRuleNameDialog() { + ZenRuleNameDialog.show(this, mRule.getName(), null, new RuleNameChangeListener()); } private void toastAndFinish() { - if (!mDeleting) { - Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) + Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) .show(); - } getActivity().finish(); } private void updateRuleName() { - Activity activity = getActivity(); - if (activity != null) { - activity.setTitle(mRule.getName()); + if (mRule != null) { mRuleName.setSummary(mRule.getName()); } else { - if (DEBUG) Log.d(TAG, "updateRuleName - activity title and mRuleName " - + "not updated; getActivity() returned null"); + if (DEBUG) Log.d(TAG, "updateRuleName - mRuleName " + + "not updated; mRuleName returned null"); } } @@ -291,10 +188,19 @@ public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase mDisableListeners = true; updateRuleName(); updateControlsInternal(); - mZenMode.setValue(Integer.toString(mRule.getInterruptionFilter())); - if (mSwitchBar != null) { - mSwitchBar.setChecked(mRule.isEnabled()); - } + updateHeader(); mDisableListeners = false; } + + public class RuleNameChangeListener implements ZenRuleNameDialog.PositiveClickListener { + public RuleNameChangeListener() {} + + @Override + public void onOk(String ruleName, Fragment parent) { + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_MODE_RULE_NAME_CHANGE_OK); + mRule.setName(ruleName); + mBackend.setZenRule(mId, mRule); + } + } } diff --git a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java index ab0349e2006..ecfe91b42b1 100644 --- a/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java +++ b/src/com/android/settings/notification/ZenModeScheduleRuleSettings.java @@ -42,6 +42,7 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settingslib.core.AbstractPreferenceController; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.List; @@ -76,21 +77,6 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase { return R.xml.zen_mode_schedule_rule_settings; } - @Override - protected List getPreferenceControllers(Context context) { - return null; - } - - @Override - protected String getZenModeDependency() { - return mDays.getKey(); - } - - @Override - protected int getEnabledToastText() { - return R.string.zen_schedule_rule_enabled_toast; - } - @Override protected void onCreateInternal() { final PreferenceScreen root = getPreferenceScreen(); @@ -208,6 +194,20 @@ public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase { updateEndSummary(); } + + @Override + protected List getPreferenceControllers(Context context) { + List controllers = new ArrayList<>(); + mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, + getLifecycle()); + mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, + R.string.zen_schedule_rule_enabled_toast, getLifecycle()); + + controllers.add(mHeader); + controllers.add(mSwitch); + return controllers; + } + @Override public int getMetricsCategory() { return MetricsEvent.NOTIFICATION_ZEN_MODE_SCHEDULE_RULE; diff --git a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java index 2b7070609d1..0ba24c07a9c 100644 --- a/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeScreenOffPreferenceController.java @@ -22,6 +22,7 @@ import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeScreenOffPreferenceController extends @@ -56,8 +57,11 @@ public class ZenModeScreenOffPreferenceController extends @Override public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean bypass = (Boolean) newValue; - if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOff=" - + !bypass); + if (ZenModeSettingsBase.DEBUG) { + Log.d(TAG, "onPrefChange allowWhenScreenOff=" + bypass); + } + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_OFF, bypass); mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_OFF, bypass); return true; } diff --git a/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java index 8e0b3485eff..bcb1af89a0b 100644 --- a/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java +++ b/src/com/android/settings/notification/ZenModeScreenOnPreferenceController.java @@ -22,6 +22,7 @@ import android.support.v14.preference.SwitchPreference; import android.support.v7.preference.Preference; import android.util.Log; +import com.android.internal.logging.nano.MetricsProto; import com.android.settingslib.core.lifecycle.Lifecycle; public class ZenModeScreenOnPreferenceController extends @@ -57,8 +58,9 @@ public class ZenModeScreenOnPreferenceController extends public boolean onPreferenceChange(Preference preference, Object newValue) { final boolean bypass = (Boolean) newValue; if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allowWhenScreenOn=" - + !bypass); - + + bypass); + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_ALLOW_WHEN_SCREEN_ON, bypass); mBackend.saveVisualEffectsPolicy(Policy.SUPPRESSED_EFFECT_SCREEN_ON, bypass); return true; } diff --git a/src/com/android/settings/notification/ZenRuleNameDialog.java b/src/com/android/settings/notification/ZenRuleNameDialog.java index eb854318a05..819ba5bfaa1 100644 --- a/src/com/android/settings/notification/ZenRuleNameDialog.java +++ b/src/com/android/settings/notification/ZenRuleNameDialog.java @@ -17,73 +17,103 @@ package com.android.settings.notification; import android.app.AlertDialog; +import android.app.Dialog; +import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.net.Uri; +import android.os.Bundle; import android.service.notification.ZenModeConfig; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -public abstract class ZenRuleNameDialog { - private static final String TAG = "ZenRuleNameDialog"; - private static final boolean DEBUG = ZenModeSettings.DEBUG; +public class ZenRuleNameDialog extends InstrumentedDialogFragment { + protected static final String TAG = "ZenRuleNameDialog"; + private static final String EXTRA_ZEN_RULE_NAME = "zen_rule_name"; + private static final String EXTRA_CONDITION_ID = "extra_zen_condition_id"; + protected static PositiveClickListener mPositiveClickListener; - private final AlertDialog mDialog; - private final EditText mEditText; - private final CharSequence mOriginalRuleName; - private final boolean mIsNew; + @Override + public int getMetricsCategory() { + return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_RULE_NAME_DIALOG; + } - public ZenRuleNameDialog(Context context, CharSequence ruleName, Uri conditionId) { - mIsNew = ruleName == null; - mOriginalRuleName = ruleName; + /** + * The interface we expect a listener to implement. + */ + public interface PositiveClickListener { + void onOk(String newName, Fragment parent); + } + + public static void show(Fragment parent, String ruleName, Uri conditionId, PositiveClickListener + listener) { + final Bundle args = new Bundle(); + args.putString(EXTRA_ZEN_RULE_NAME, ruleName); + args.putParcelable(EXTRA_CONDITION_ID, conditionId); + mPositiveClickListener = listener; + + ZenRuleNameDialog dialog = new ZenRuleNameDialog(); + dialog.setArguments(args); + dialog.setTargetFragment(parent, 0); + dialog.show(parent.getFragmentManager(), TAG); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle arguments = getArguments(); + Uri conditionId = arguments.getParcelable(EXTRA_CONDITION_ID); + String ruleName = arguments.getString(EXTRA_ZEN_RULE_NAME); + + boolean isNew = ruleName == null; + CharSequence originalRuleName = ruleName; + Context context = getContext(); final View v = LayoutInflater.from(context).inflate(R.layout.zen_rule_name, null, false); - mEditText = (EditText) v.findViewById(R.id.zen_mode_rule_name); - if (!mIsNew) { - mEditText.setText(ruleName); + EditText editText = (EditText) v.findViewById(R.id.zen_mode_rule_name); + if (!isNew) { + // set text to current rule name + editText.setText(ruleName); + // move cursor to end of text + editText.setSelection(editText.getText().length()); } - mEditText.setSelectAllOnFocus(true); - mDialog = new AlertDialog.Builder(context) - .setTitle(getTitleResource(conditionId)) + editText.setSelectAllOnFocus(true); + return new AlertDialog.Builder(context) + .setTitle(getTitleResource(conditionId, isNew)) .setView(v) - .setPositiveButton(mIsNew ? R.string.zen_mode_add : R.string.okay, + .setPositiveButton(isNew ? R.string.zen_mode_add : R.string.okay, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final String newName = trimmedText(); - if (TextUtils.isEmpty(newName)) { - return; - } - if (!mIsNew && mOriginalRuleName != null - && mOriginalRuleName.equals(newName)) { - return; // no change to an existing rule, just dismiss - } - onOk(newName); - } - }) + @Override + public void onClick(DialogInterface dialog, int which) { + final String newName = trimmedText(editText); + if (TextUtils.isEmpty(newName)) { + return; + } + if (!isNew && originalRuleName != null + && originalRuleName.equals(newName)) { + return; // no change to an existing rule, just dismiss + } + mPositiveClickListener.onOk(newName, getTargetFragment()); + } + }) .setNegativeButton(R.string.cancel, null) .create(); } - abstract public void onOk(String ruleName); - - public void show() { - mDialog.show(); + private String trimmedText(EditText editText) { + return editText.getText() == null ? null : editText.getText().toString().trim(); } - private String trimmedText() { - return mEditText.getText() == null ? null : mEditText.getText().toString().trim(); - } - - private int getTitleResource(Uri conditionId) { + private int getTitleResource(Uri conditionId, boolean isNew) { final boolean isEvent = ZenModeConfig.isValidEventConditionId(conditionId); final boolean isTime = ZenModeConfig.isValidScheduleConditionId(conditionId); int titleResource = R.string.zen_mode_rule_name; - if (mIsNew) { + if (isNew) { if (isEvent) { titleResource = R.string.zen_mode_add_event_rule; } else if (isTime) { diff --git a/src/com/android/settings/notification/ZenRulePreference.java b/src/com/android/settings/notification/ZenRulePreference.java index 4d7181a0cdc..90f6a94c37b 100644 --- a/src/com/android/settings/notification/ZenRulePreference.java +++ b/src/com/android/settings/notification/ZenRulePreference.java @@ -16,23 +16,21 @@ package com.android.settings.notification; -import android.app.AlertDialog; import android.app.AutomaticZenRule; -import android.app.NotificationManager; +import android.app.Fragment; import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; -import android.content.res.Resources; import android.service.notification.ZenModeConfig; import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceCategory; import android.support.v7.preference.PreferenceViewHolder; import android.view.View; +import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.core.instrumentation.MetricsFeatureProvider; import com.android.settings.utils.ManagedServiceSettings; import com.android.settings.utils.ZenServiceListing; import com.android.settingslib.TwoTargetPreference; @@ -45,16 +43,17 @@ public class ZenRulePreference extends TwoTargetPreference { final CharSequence mName; final String mId; boolean appExists; - final PreferenceCategory mParent; + final Fragment mParent; final Preference mPref; final Context mContext; final ZenModeBackend mBackend; final ZenServiceListing mServiceListing; final PackageManager mPm; + final MetricsFeatureProvider mMetricsFeatureProvider; public ZenRulePreference(Context context, final Map.Entry ruleEntry, - PreferenceCategory prefCategory) { + Fragment parent, MetricsFeatureProvider metricsProvider) { super(context); mBackend = ZenModeBackend.getInstance(context); @@ -62,11 +61,12 @@ public class ZenRulePreference extends TwoTargetPreference { final AutomaticZenRule rule = ruleEntry.getValue(); mName = rule.getName(); mId = ruleEntry.getKey(); - mParent = prefCategory; + mParent = parent; mPm = mContext.getPackageManager(); mServiceListing = new ZenServiceListing(mContext, CONFIG); mServiceListing.reloadApprovedServices(); mPref = this; + mMetricsFeatureProvider = metricsProvider; setAttributes(rule); } @@ -89,25 +89,21 @@ public class ZenRulePreference extends TwoTargetPreference { private final View.OnClickListener mDeleteListener = new View.OnClickListener() { @Override public void onClick(View v) { - showDeleteRuleDialog(mId, mName, mParent, mPref); + showDeleteRuleDialog(mParent, mId, mName.toString()); } }; - private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName, - PreferenceCategory parent, Preference pref) { - new AlertDialog.Builder(mContext) - .setMessage(mContext.getResources().getString( - R.string.zen_mode_delete_rule_confirmation, ruleName)) - .setNegativeButton(R.string.cancel, null) - .setPositiveButton(R.string.zen_mode_delete_rule_button, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - mBackend.removeZenRule(ruleId); - parent.removePreference(pref); - } - }) - .show(); + private void showDeleteRuleDialog(final Fragment parent, final String ruleId, + final String ruleName) { + ZenDeleteRuleDialog.show(parent, ruleName, ruleId, + new ZenDeleteRuleDialog.PositiveClickListener() { + @Override + public void onOk(String id) { + mMetricsFeatureProvider.action(mContext, + MetricsProto.MetricsEvent.ACTION_ZEN_DELETE_RULE_OK); + mBackend.removeZenRule(id); + } + }); } protected void setAttributes(AutomaticZenRule rule) { @@ -141,26 +137,8 @@ public class ZenRulePreference extends TwoTargetPreference { private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, CharSequence providerLabel) { - final String mode = computeZenModeCaption(mContext.getResources(), - rule.getInterruptionFilter()); - final String ruleState = (rule == null || !rule.isEnabled()) + return (rule == null || !rule.isEnabled()) ? mContext.getResources().getString(R.string.switch_off_text) - : mContext.getResources().getString( - R.string.zen_mode_rule_summary_enabled_combination, mode); - - return ruleState; - } - - private static String computeZenModeCaption(Resources res, int zenMode) { - switch (zenMode) { - case NotificationManager.INTERRUPTION_FILTER_ALARMS: - return res.getString(R.string.zen_mode_option_alarms); - case NotificationManager.INTERRUPTION_FILTER_PRIORITY: - return res.getString(R.string.zen_mode_option_important_interruptions); - case NotificationManager.INTERRUPTION_FILTER_NONE: - return res.getString(R.string.zen_mode_option_no_interruptions); - default: - return null; - } + : mContext.getResources().getString(R.string.switch_on_text); } } \ No newline at end of file diff --git a/src/com/android/settings/notification/ZenRuleSelectionDialog.java b/src/com/android/settings/notification/ZenRuleSelectionDialog.java index 0c725ed6f5e..0784d5a4674 100644 --- a/src/com/android/settings/notification/ZenRuleSelectionDialog.java +++ b/src/com/android/settings/notification/ZenRuleSelectionDialog.java @@ -16,16 +16,20 @@ package com.android.settings.notification; +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import android.app.AlertDialog; +import android.app.Dialog; +import android.app.Fragment; import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; -import android.content.DialogInterface.OnDismissListener; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Bundle; import android.service.notification.ZenModeConfig; import android.util.Log; import android.view.LayoutInflater; @@ -35,6 +39,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.settings.R; +import com.android.settings.core.instrumentation.InstrumentedDialogFragment; import com.android.settings.utils.ZenServiceListing; import java.lang.ref.WeakReference; @@ -43,24 +48,48 @@ import java.util.Comparator; import java.util.Set; import java.util.TreeSet; -public abstract class ZenRuleSelectionDialog { +public class ZenRuleSelectionDialog extends InstrumentedDialogFragment { private static final String TAG = "ZenRuleSelectionDialog"; private static final boolean DEBUG = ZenModeSettings.DEBUG; - private final Context mContext; - private final PackageManager mPm; - private NotificationManager mNm; - private final AlertDialog mDialog; - private final LinearLayout mRuleContainer; - private final ZenServiceListing mServiceListing; + private static ZenServiceListing mServiceListing; + protected static PositiveClickListener mPositiveClickListener; - public ZenRuleSelectionDialog(Context context, ZenServiceListing serviceListing) { + private static Context mContext; + private static PackageManager mPm; + private static NotificationManager mNm; + private LinearLayout mRuleContainer; + + /** + * The interface we expect a listener to implement. + */ + public interface PositiveClickListener { + void onSystemRuleSelected(ZenRuleInfo ruleInfo, Fragment parent); + void onExternalRuleSelected(ZenRuleInfo ruleInfo, Fragment parent); + } + + @Override + public int getMetricsCategory() { + return MetricsEvent.NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG; + } + + public static void show(Context context, Fragment parent, PositiveClickListener + listener, ZenServiceListing serviceListing) { + mPositiveClickListener = listener; mContext = context; - mPm = context.getPackageManager(); - mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + mPm = mContext.getPackageManager(); + mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mServiceListing = serviceListing; - final View v = - LayoutInflater.from(context).inflate(R.layout.zen_rule_type_selection, null, false); + + ZenRuleSelectionDialog dialog = new ZenRuleSelectionDialog(); + dialog.setTargetFragment(parent, 0); + dialog.show(parent.getFragmentManager(), TAG); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final View v = LayoutInflater.from(getContext()).inflate(R.layout.zen_rule_type_selection, + null, false); mRuleContainer = (LinearLayout) v.findViewById(R.id.rule_container); if (mServiceListing != null) { @@ -69,28 +98,21 @@ public abstract class ZenRuleSelectionDialog { mServiceListing.addZenCallback(mServiceListingCallback); mServiceListing.reloadApprovedServices(); } - mDialog = new AlertDialog.Builder(context) + return new AlertDialog.Builder(getContext()) .setTitle(R.string.zen_mode_choose_rule_type) .setView(v) - .setOnDismissListener(new OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - if (mServiceListing != null) { - mServiceListing.removeZenCallback(mServiceListingCallback); - } - } - }) .setNegativeButton(R.string.cancel, null) .create(); } - public void show() { - mDialog.show(); + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (mServiceListing != null) { + mServiceListing.removeZenCallback(mServiceListingCallback); + } } - abstract public void onSystemRuleSelected(ZenRuleInfo ruleInfo); - abstract public void onExternalRuleSelected(ZenRuleInfo ruleInfo); - private void bindType(final ZenRuleInfo ri) { try { ApplicationInfo info = mPm.getApplicationInfo(ri.packageName, 0); @@ -108,11 +130,11 @@ public abstract class ZenRuleSelectionDialog { v.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mDialog.dismiss(); + dismiss(); if (ri.isSystem) { - onSystemRuleSelected(ri); + mPositiveClickListener.onSystemRuleSelected(ri, getTargetFragment()); } else { - onExternalRuleSelected(ri); + mPositiveClickListener.onExternalRuleSelected(ri, getTargetFragment()); } } }); diff --git a/tests/robotests/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceControllerTest.java new file mode 100644 index 00000000000..0dae9235dba --- /dev/null +++ b/tests/robotests/src/com/android/settings/notification/ZenModeAutomaticRulesPreferenceControllerTest.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2017 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 junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.AutomaticZenRule; +import android.app.Fragment; +import android.app.NotificationManager; +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; +import android.support.v7.preference.PreferenceCategory; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; +import org.robolectric.util.ReflectionHelpers; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class ZenModeAutomaticRulesPreferenceControllerTest { + private ZenModeAutomaticRulesPreferenceController mController; + private final String GENERIC_RULE_NAME = "test"; + + @Mock + private ZenModeBackend mBackend; + @Mock + private NotificationManager mNotificationManager; + @Mock + private PreferenceCategory mockPref; + @Mock + private NotificationManager.Policy mPolicy; + @Mock + private PreferenceScreen mPreferenceScreen; + + private Context mContext; + private ContentResolver mContentResolver; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + ShadowApplication shadowApplication = ShadowApplication.getInstance(); + shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNotificationManager); + + mContext = shadowApplication.getApplicationContext(); + mContentResolver = RuntimeEnvironment.application.getContentResolver(); + when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy); + mController = new ZenModeAutomaticRulesPreferenceController(mContext, mock(Fragment.class), + mock(Lifecycle.class)); + ReflectionHelpers.setField(mController, "mBackend", mBackend); + + when(mPreferenceScreen.findPreference(mController.getPreferenceKey())).thenReturn( + mockPref); + mController.displayPreference(mPreferenceScreen); + } + + @Test + public void updateState_checkRuleOrderingDescending() { + final int NUM_RULES = 4; + when(mNotificationManager.getAutomaticZenRules()).thenReturn( + mockAutoZenRulesDecreasingCreationTime(NUM_RULES)); + + Map.Entry[] rules = mController.sortedRules(); + assertEquals(NUM_RULES, rules.length); + + // check ordering, most recent should be at the bottom/end (ie higher creation time) + for (int i = 0; i < NUM_RULES; i++) { + assertEquals(rules[i].getKey(), GENERIC_RULE_NAME + (NUM_RULES - 1 - i)); + } + } + + @Test + public void updateState_checkRuleOrderingAscending() { + final int NUM_RULES = 4; + when(mNotificationManager.getAutomaticZenRules()).thenReturn( + mockAutoZenRulesAscendingCreationTime(NUM_RULES)); + + Map.Entry[] rules = mController.sortedRules(); + assertEquals(NUM_RULES, rules.length); + + // check ordering, most recent should be at the bottom/end (ie higher creation time) + for (int i = 0; i < NUM_RULES; i++) { + assertEquals(rules[i].getKey(), GENERIC_RULE_NAME + i); + } + } + + @Test + public void updateState_checkRuleOrderingMix() { + final int NUM_RULES = 4; + // map with creation times: 0, 2, 4, 6 + Map rMap = mockAutoZenRulesAscendingCreationTime(NUM_RULES); + + final String insertedRule1 = "insertedRule1"; + rMap.put(insertedRule1, new AutomaticZenRule(insertedRule1, null, null, + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 5)); + + final String insertedRule2 = "insertedRule2"; + rMap.put(insertedRule2, new AutomaticZenRule(insertedRule2, null, null, + Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, 3)); + + // rule map with rule creation times, 0, 2, 4, 6, 5, 3 + // sort should create ordering based on creation times: 0, 2, 3, 4, 5, 6 + when(mNotificationManager.getAutomaticZenRules()).thenReturn(rMap); + + Map.Entry[] rules = mController.sortedRules(); + assertEquals(NUM_RULES + 2, rules.length); // inserted 2 rules + + // check ordering of inserted rules + assertEquals(rules[4].getKey(), insertedRule1); + assertEquals(rules[2].getKey(), insertedRule2); + } + + private Map mockAutoZenRulesAscendingCreationTime(int numRules) { + Map ruleMap = new HashMap<>(); + + for (int i = 0; i < numRules; i++) { + ruleMap.put(GENERIC_RULE_NAME + i, new AutomaticZenRule(GENERIC_RULE_NAME + i, null, + null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, i * 2)); + } + + return ruleMap; + } + + private Map mockAutoZenRulesDecreasingCreationTime(int numRules) { + Map ruleMap = new HashMap<>(); + + for (int i = 0; i < numRules; i++) { + ruleMap.put(GENERIC_RULE_NAME + i, new AutomaticZenRule(GENERIC_RULE_NAME + i, null, + null, Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, true, numRules - i)); + } + + return ruleMap; + } +} \ No newline at end of file