Adding zen dialog in zen settings

Test: make ROBOTEST_FILTER=EnableZenModeDialogTest RunSettingsRoboTests -j40
Bug: 63077372
Change-Id: Ib3193788bb0a31e20683d3191eb1238d6a63f1e7
This commit is contained in:
Beverly
2018-01-22 10:30:06 -05:00
parent 190a211a03
commit 2607287e5a
7 changed files with 649 additions and 13 deletions

View File

@@ -30,7 +30,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="left"
android:text="@string/zen_mode_button_turn_on" android:text="@string/zen_mode_button_turn_on"
android:paddingEnd="8dp" /> android:paddingEnd="8dp" />
@@ -40,7 +40,7 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="1" android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="left"
android:text="@string/zen_mode_button_turn_off" android:text="@string/zen_mode_button_turn_off"
android:paddingEnd="8dp" /> android:paddingEnd="8dp" />

View File

@@ -6845,6 +6845,9 @@
<!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]--> <!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
<string name="zen_mode_settings_title">Do Not Disturb</string> <string name="zen_mode_settings_title">Do Not Disturb</string>
<!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
<string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
<!-- Do not disturb: Title for the behaviors option and associated settings page. [CHAR LIMIT=30] --> <!-- Do not disturb: Title for the behaviors option and associated settings page. [CHAR LIMIT=30] -->
<string name="zen_mode_behavior_settings_title">Behavior</string> <string name="zen_mode_behavior_settings_title">Behavior</string>
@@ -6902,6 +6905,9 @@
<!-- Do not disturb: Button to add new automatic rule to DND. [CHAR LIMIT=30] --> <!-- Do not disturb: Button to add new automatic rule to DND. [CHAR LIMIT=30] -->
<string name="zen_mode_add">Add</string> <string name="zen_mode_add">Add</string>
<!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_enable_dialog_turn_on">Turn on</string>
<!-- Do not disturb: Label for button that will turn on zen mode. [CHAR LIMIT=30] --> <!-- Do not disturb: Label for button that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_button_turn_on">Turn on now</string> <string name="zen_mode_button_turn_on">Turn on now</string>
@@ -6920,6 +6926,12 @@
<!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by an app --> <!-- [CHAR LIMIT=110] Zen mode settings footer: Footer how DND was triggered by an app -->
<string name="zen_mode_settings_dnd_automatic_rule_app">Do Not Disturb was automatically turned on by an app (<xliff:g id="app_name" example="Android Services">%s</xliff:g>)</string> <string name="zen_mode_settings_dnd_automatic_rule_app">Do Not Disturb was automatically turned on by an app (<xliff:g id="app_name" example="Android Services">%s</xliff:g>)</string>
<!--[CHAR LIMIT=40] Zen Interruption level: Priority. -->
<string name="zen_interruption_level_priority">Priority only</string>
<!-- [CHAR LIMIT=20] Accessibility string for current zen mode and selected exit condition. A template that simply concatenates existing mode string and the current condition description. -->
<string name="zen_mode_and_condition"><xliff:g id="zen_mode" example="Priority interruptions only">%1$s</xliff:g>. <xliff:g id="exit_condition" example="For one hour">%2$s</xliff:g></string>
<!-- Work Sounds: Work sound settings section header. [CHAR LIMIT=50] --> <!-- Work Sounds: Work sound settings section header. [CHAR LIMIT=50] -->
<string name="sound_work_settings">Work profile sounds</string> <string name="sound_work_settings">Work profile sounds</string>

View File

@@ -0,0 +1,467 @@
package com.android.settings.notification;
/*
* 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.
*/
import static android.util.Log.wtf;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.ScrollView;
import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Objects;
public class EnableZenModeDialog extends InstrumentedDialogFragment {
private static final String TAG = "EnableZenModeDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS;
private static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0];
private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
@VisibleForTesting
public static final int FOREVER_CONDITION_INDEX = 0;
@VisibleForTesting
public static final int COUNTDOWN_CONDITION_INDEX = 1;
@VisibleForTesting
public static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
@VisibleForTesting
protected Activity mActivity;
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
@VisibleForTesting
protected Uri mForeverId;
private int mBucketIndex = -1;
private AlarmManager mAlarmManager;
private int mUserId;
private boolean mAttached;
@VisibleForTesting
protected Context mContext;
private RadioGroup mZenRadioGroup;
@VisibleForTesting
protected LinearLayout mZenRadioGroupContent;
private int MAX_MANUAL_DND_OPTIONS = 3;
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
NotificationManager noMan = (NotificationManager) getContext().
getSystemService(Context.NOTIFICATION_SERVICE);
mContext = getContext();
mForeverId = Condition.newId(mContext).appendPath("forever").build();
mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
mUserId = mContext.getUserId();
mAttached = false;
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setTitle(R.string.zen_mode_settings_turn_on_dialog_title)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.zen_mode_enable_dialog_turn_on,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int checkedId = mZenRadioGroup.getCheckedRadioButtonId();
ConditionTag tag = getConditionTagAt(checkedId);
if (isForever(tag.condition)) {
MetricsLogger.action(getContext(),
MetricsProto.MetricsEvent.
NOTIFICATION_ZEN_MODE_TOGGLE_ON_FOREVER);
} else if (isAlarm(tag.condition)) {
MetricsLogger.action(getContext(),
MetricsProto.MetricsEvent.
NOTIFICATION_ZEN_MODE_TOGGLE_ON_ALARM);
} else if (isCountdown(tag.condition)) {
MetricsLogger.action(getContext(),
MetricsProto.MetricsEvent.
NOTIFICATION_ZEN_MODE_TOGGLE_ON_COUNTDOWN);
} else {
wtf(TAG, "Invalid manual condition: " + tag.condition);
}
// always triggers priority-only dnd with chosen condition
noMan.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
getRealConditionId(tag.condition), TAG);
}
});
View contentView = getContentView();
bindConditions(forever());
builder.setView(contentView);
return builder.create();
}
private void hideAllConditions() {
final int N = mZenRadioGroupContent.getChildCount();
for (int i = 0; i < N; i++) {
mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE);
}
}
protected View getContentView() {
if (mActivity == null) {
mActivity = getActivity();
}
final LayoutInflater inflater = mActivity.getLayoutInflater();
View contentView = inflater.inflate(R.layout.zen_mode_turn_on_dialog_container, null);
ScrollView container = (ScrollView) contentView.findViewById(R.id.container);
mZenRadioGroup = container.findViewById(R.id.zen_radio_buttons);
mZenRadioGroupContent = container.findViewById(R.id.zen_radio_buttons_content);
for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) {
final View radioButton = inflater.inflate(R.layout.zen_mode_radio_button,
mZenRadioGroup, false);
mZenRadioGroup.addView(radioButton);
radioButton.setId(i);
final View radioButtonContent = inflater.inflate(R.layout.zen_mode_condition,
mZenRadioGroupContent, false);
radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS);
mZenRadioGroupContent.addView(radioButtonContent);
}
hideAllConditions();
return contentView;
}
@Override
public int getMetricsCategory() {
return MetricsProto.MetricsEvent.NOTIFICATION_ZEN_MODE_ENABLE_DIALOG;
}
@VisibleForTesting
protected void bind(final Condition condition, final View row, final int rowId) {
if (condition == null) throw new IllegalArgumentException("condition must not be null");
final boolean enabled = condition.state == Condition.STATE_TRUE;
final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() :
new ConditionTag();
row.setTag(tag);
final boolean first = tag.rb == null;
if (tag.rb == null) {
tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowId);
}
tag.condition = condition;
final Uri conditionId = getConditionId(tag.condition);
if (DEBUG) Log.d(TAG, "bind i=" + mZenRadioGroupContent.indexOfChild(row) + " first="
+ first + " condition=" + conditionId);
tag.rb.setEnabled(enabled);
tag.rb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
tag.rb.setChecked(true);
if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId);
MetricsLogger.action(mContext,
MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
announceConditionSelection(tag);
}
}
});
updateUi(tag, row, condition, enabled, rowId, conditionId);
row.setVisibility(View.VISIBLE);
}
@VisibleForTesting
protected ConditionTag getConditionTagAt(int index) {
return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
}
@VisibleForTesting
protected void bindConditions(Condition c) {
// forever
bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
FOREVER_CONDITION_INDEX);
if (c == null) {
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else if (isForever(c)) {
getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
bindGenericCountdown();
bindNextAlarm(getTimeUntilNextAlarmCondition());
} else {
if (isAlarm(c)) {
bindGenericCountdown();
bindNextAlarm(c);
getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
} else if (isCountdown(c)) {
bindNextAlarm(getTimeUntilNextAlarmCondition());
bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
COUNTDOWN_CONDITION_INDEX);
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
} else {
wtf(TAG, "Invalid manual condition: " + c);
}
}
}
public static Uri getConditionId(Condition condition) {
return condition != null ? condition.id : null;
}
public Condition forever() {
Uri foreverId = Condition.newId(mContext).appendPath("forever").build();
return new Condition(foreverId, foreverSummary(mContext), "", "", 0 /*icon*/,
Condition.STATE_TRUE, 0 /*flags*/);
}
public long getNextAlarm() {
final AlarmManager.AlarmClockInfo info = mAlarmManager.getNextAlarmClock(mUserId);
return info != null ? info.getTriggerTime() : 0;
}
@VisibleForTesting
protected boolean isAlarm(Condition c) {
return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
}
@VisibleForTesting
protected boolean isCountdown(Condition c) {
return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
}
private boolean isForever(Condition c) {
return c != null && mForeverId.equals(c.id);
}
private Uri getRealConditionId(Condition condition) {
return isForever(condition) ? null : getConditionId(condition);
}
private String foreverSummary(Context context) {
return context.getString(com.android.internal.R.string.zen_mode_forever);
}
private static void setToMidnight(Calendar calendar) {
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
}
// Returns a time condition if the next alarm is within the next week.
@VisibleForTesting
protected Condition getTimeUntilNextAlarmCondition() {
GregorianCalendar weekRange = new GregorianCalendar();
setToMidnight(weekRange);
weekRange.add(Calendar.DATE, 6);
final long nextAlarmMs = getNextAlarm();
if (nextAlarmMs > 0) {
GregorianCalendar nextAlarm = new GregorianCalendar();
nextAlarm.setTimeInMillis(nextAlarmMs);
setToMidnight(nextAlarm);
if (weekRange.compareTo(nextAlarm) >= 0) {
return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs,
ActivityManager.getCurrentUser());
}
}
return null;
}
@VisibleForTesting
protected void bindGenericCountdown() {
mBucketIndex = DEFAULT_BUCKET_INDEX;
Condition countdown = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) {
bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
COUNTDOWN_CONDITION_INDEX);
}
}
private void updateUi(ConditionTag tag, View row, Condition condition,
boolean enabled, int rowId, Uri conditionId) {
if (tag.lines == null) {
tag.lines = row.findViewById(android.R.id.content);
}
if (tag.line1 == null) {
tag.line1 = (TextView) row.findViewById(android.R.id.text1);
}
if (tag.line2 == null) {
tag.line2 = (TextView) row.findViewById(android.R.id.text2);
}
final String line1 = !TextUtils.isEmpty(condition.line1) ? condition.line1
: condition.summary;
final String line2 = condition.line2;
tag.line1.setText(line1);
if (TextUtils.isEmpty(line2)) {
tag.line2.setVisibility(View.GONE);
} else {
tag.line2.setVisibility(View.VISIBLE);
tag.line2.setText(line2);
}
tag.lines.setEnabled(enabled);
tag.lines.setAlpha(enabled ? 1 : .4f);
tag.lines.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tag.rb.setChecked(true);
}
});
// minus button
final ImageView button1 = (ImageView) row.findViewById(android.R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, false /*down*/, rowId);
}
});
// plus button
final ImageView button2 = (ImageView) row.findViewById(android.R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onClickTimeButton(row, tag, true /*up*/, rowId);
}
});
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
if (rowId == COUNTDOWN_CONDITION_INDEX && time > 0) {
button1.setVisibility(View.VISIBLE);
button2.setVisibility(View.VISIBLE);
if (mBucketIndex > -1) {
button1.setEnabled(mBucketIndex > 0);
button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
} else {
final long span = time - System.currentTimeMillis();
button1.setEnabled(span > MIN_BUCKET_MINUTES * MINUTES_MS);
final Condition maxCondition = ZenModeConfig.toTimeCondition(mContext,
MAX_BUCKET_MINUTES, ActivityManager.getCurrentUser());
button2.setEnabled(!Objects.equals(condition.summary, maxCondition.summary));
}
button1.setAlpha(button1.isEnabled() ? 1f : .5f);
button2.setAlpha(button2.isEnabled() ? 1f : .5f);
} else {
button1.setVisibility(View.GONE);
button2.setVisibility(View.GONE);
}
}
@VisibleForTesting
protected void bindNextAlarm(Condition c) {
View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
ConditionTag tag = (ConditionTag) alarmContent.getTag();
if (c != null && (!mAttached || tag == null || tag.condition == null)) {
bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX);
}
// hide the alarm radio button if there isn't a "next alarm condition"
tag = (ConditionTag) alarmContent.getTag();
boolean showAlarm = tag != null && tag.condition != null;
mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
showAlarm ? View.VISIBLE : View.GONE);
alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
}
private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.QS_DND_TIME, up);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
if (mBucketIndex == -1) {
// not on a known index, search for the next or prev bucket by time
final Uri conditionId = getConditionId(tag.condition);
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
final long now = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
int j = up ? i : N - 1 - i;
final int bucketMinutes = MINUTE_BUCKETS[j];
final long bucketTime = now + bucketMinutes * MINUTES_MS;
if (up && bucketTime > time || !up && bucketTime < time) {
mBucketIndex = j;
newCondition = ZenModeConfig.toTimeCondition(mContext,
bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
false /*shortVersion*/);
break;
}
}
if (newCondition == null) {
mBucketIndex = DEFAULT_BUCKET_INDEX;
newCondition = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
} else {
// on a known index, simply increment or decrement
mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1)));
newCondition = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
bind(newCondition, row, rowId);
tag.rb.setChecked(true);
announceConditionSelection(tag);
}
private void announceConditionSelection(ConditionTag tag) {
// condition will always be priority-only
String modeText = mContext.getString(R.string.zen_interruption_level_priority);
if (tag.line1 != null) {
mZenRadioGroupContent.announceForAccessibility(mContext.getString(
R.string.zen_mode_and_condition, modeText, tag.line1.getText()));
}
}
// used as the view tag on condition rows
@VisibleForTesting
protected static class ConditionTag {
public RadioButton rb;
public View lines;
public TextView line1;
public TextView line2;
public Condition condition;
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification; package com.android.settings.notification;
import android.app.FragmentManager;
import android.content.Context; import android.content.Context;
import android.provider.Settings; import android.provider.Settings;
import android.support.v7.preference.Preference; import android.support.v7.preference.Preference;
@@ -31,12 +32,16 @@ import com.android.settingslib.core.lifecycle.Lifecycle;
public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController public class ZenModeButtonPreferenceController extends AbstractZenModePreferenceController
implements PreferenceControllerMixin { implements PreferenceControllerMixin {
private static final String TAG = "EnableZenModeButton";
protected static final String KEY = "zen_mode_settings_button_container"; protected static final String KEY = "zen_mode_settings_button_container";
private Button mZenButtonOn; private Button mZenButtonOn;
private Button mZenButtonOff; private Button mZenButtonOff;
private FragmentManager mFragment;
public ZenModeButtonPreferenceController(Context context, Lifecycle lifecycle) { public ZenModeButtonPreferenceController(Context context, Lifecycle lifecycle, FragmentManager
fragment) {
super(context, KEY, lifecycle); super(context, KEY, lifecycle);
mFragment = fragment;
} }
@Override @Override
@@ -56,11 +61,8 @@ public class ZenModeButtonPreferenceController extends AbstractZenModePreference
if (null == mZenButtonOn) { if (null == mZenButtonOn) {
mZenButtonOn = (Button) ((LayoutPreference) preference) mZenButtonOn = (Button) ((LayoutPreference) preference)
.findViewById(R.id.zen_mode_settings_turn_on_button); .findViewById(R.id.zen_mode_settings_turn_on_button);
mZenButtonOn.setOnClickListener(v -> { mZenButtonOn.setOnClickListener(v ->
mMetricsFeatureProvider.action(mContext, new EnableZenModeDialog().show(mFragment, TAG));
MetricsProto.MetricsEvent.ACTION_ZEN_TOGGLE_DND_BUTTON, true);
mBackend.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
});
} }
if (null == mZenButtonOff) { if (null == mZenButtonOff) {

View File

@@ -17,6 +17,7 @@
package com.android.settings.notification; package com.android.settings.notification;
import android.app.AutomaticZenRule; import android.app.AutomaticZenRule;
import android.app.FragmentManager;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.app.NotificationManager.Policy; import android.app.NotificationManager.Policy;
import android.content.Context; import android.content.Context;
@@ -50,7 +51,7 @@ public class ZenModeSettings extends ZenModeSettingsBase {
@Override @Override
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) { protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getLifecycle()); return buildPreferenceControllers(context, getLifecycle(), getFragmentManager());
} }
@Override @Override
@@ -59,11 +60,11 @@ public class ZenModeSettings extends ZenModeSettingsBase {
} }
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context, private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
Lifecycle lifecycle) { Lifecycle lifecycle, FragmentManager fragmentManager) {
List<AbstractPreferenceController> controllers = new ArrayList<>(); List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeBehaviorPreferenceController(context, lifecycle)); controllers.add(new ZenModeBehaviorPreferenceController(context, lifecycle));
controllers.add(new ZenModeAutomationPreferenceController(context)); controllers.add(new ZenModeAutomationPreferenceController(context));
controllers.add(new ZenModeButtonPreferenceController(context, lifecycle)); controllers.add(new ZenModeButtonPreferenceController(context, lifecycle, fragmentManager));
controllers.add(new ZenModeSettingsFooterPreferenceController(context, lifecycle)); controllers.add(new ZenModeSettingsFooterPreferenceController(context, lifecycle));
return controllers; return controllers;
} }
@@ -211,7 +212,7 @@ public class ZenModeSettings extends ZenModeSettingsBase {
@Override @Override
public List<AbstractPreferenceController> getPreferenceControllers(Context public List<AbstractPreferenceController> getPreferenceControllers(Context
context) { context) {
return buildPreferenceControllers(context, null); return buildPreferenceControllers(context, null, null);
} }
}; };
} }

View File

@@ -0,0 +1,152 @@
/*
* 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 junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.net.Uri;
import android.service.notification.Condition;
import android.view.LayoutInflater;
import com.android.settings.TestConfig;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
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;
@RunWith(SettingsRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class EnableZenModeDialogTest {
private EnableZenModeDialog mController;
@Mock
private Context mContext;
@Mock
private Activity mActivity;
@Mock
private Fragment mFragment;
private Context mShadowContext;
private LayoutInflater mLayoutInflater;
private Condition mCountdownCondition;
private Condition mAlarmCondition;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mShadowContext = RuntimeEnvironment.application;
when(mActivity.getApplicationContext()).thenReturn(mShadowContext);
when(mContext.getApplicationContext()).thenReturn(mContext);
when(mFragment.getContext()).thenReturn(mShadowContext);
mLayoutInflater = LayoutInflater.from(mShadowContext);
when(mActivity.getLayoutInflater()).thenReturn(mLayoutInflater);
mController = spy(new EnableZenModeDialog());
mController.mContext = mContext;
mController.mActivity = mActivity;
mController.mForeverId = Condition.newId(mContext).appendPath("forever").build();
when(mContext.getString(com.android.internal.R.string.zen_mode_forever))
.thenReturn("testSummary");
mController.getContentView();
// these methods use static calls to ZenModeConfig which would normally fail in robotests,
// so instead do nothing:
doNothing().when(mController).bindGenericCountdown();
doReturn(null).when(mController).getTimeUntilNextAlarmCondition();
doNothing().when(mController).bindNextAlarm(any());
// as a result of doing nothing above, must bind manually:
Uri alarm = Condition.newId(mContext).appendPath("alarm").build();
mAlarmCondition = new Condition(alarm, "alarm", "", "", 0, 0, 0);
Uri countdown = Condition.newId(mContext).appendPath("countdown").build();
mCountdownCondition = new Condition(countdown, "countdown", "", "", 0, 0, 0);
mController.bind(mCountdownCondition,
mController.mZenRadioGroupContent.getChildAt(
EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX),
EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX);
mController.bind(mAlarmCondition,
mController.mZenRadioGroupContent.getChildAt(
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX),
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX);
}
@Test
public void testForeverChecked() {
mController.bindConditions(mController.forever());
assertTrue(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
public void testNoneChecked() {
mController.bindConditions(null);
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
public void testAlarmChecked() {
doReturn(false).when(mController).isCountdown(mAlarmCondition);
doReturn(true).when(mController).isAlarm(mAlarmCondition);
mController.bindConditions(mAlarmCondition);
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
.isChecked());
assertTrue(mController.getConditionTagAt(
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
@Test
public void testCountdownChecked() {
doReturn(false).when(mController).isAlarm(mCountdownCondition);
doReturn(true).when(mController).isCountdown(mCountdownCondition);
mController.bindConditions(mCountdownCondition);
assertFalse(mController.getConditionTagAt(EnableZenModeDialog.FOREVER_CONDITION_INDEX).rb
.isChecked());
assertTrue(mController.getConditionTagAt(EnableZenModeDialog.COUNTDOWN_CONDITION_INDEX).rb
.isChecked());
assertFalse(mController.getConditionTagAt(
EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
}
}

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.app.FragmentManager;
import android.app.NotificationManager; import android.app.NotificationManager;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -79,7 +80,8 @@ public class ZenModeButtonPreferenceControllerTest {
mContext = shadowApplication.getApplicationContext(); mContext = shadowApplication.getApplicationContext();
mContentResolver = RuntimeEnvironment.application.getContentResolver(); mContentResolver = RuntimeEnvironment.application.getContentResolver();
mController = new ZenModeButtonPreferenceController(mContext, mock(Lifecycle.class)); mController = new ZenModeButtonPreferenceController(mContext, mock(Lifecycle.class),
mock(FragmentManager.class));
when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy); when(mNotificationManager.getNotificationPolicy()).thenReturn(mPolicy);
ReflectionHelpers.setField(mController, "mBackend", mBackend); ReflectionHelpers.setField(mController, "mBackend", mBackend);
ReflectionHelpers.setField(mController, "mZenButtonOn", mZenButtonOn); ReflectionHelpers.setField(mController, "mZenButtonOn", mZenButtonOn);