From 09da25f00d0d8cd6625b6ba6f184d4a182b04e7f Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 26 Feb 2018 09:17:07 -0500 Subject: [PATCH] Using zen duration preference Test: make ROBOTEST_FILTER=ZenDurationDialogTest RunSettingsLibRoboTests -j40 Bug: 73741459 Change-Id: Ide76ac8016b84f128c47ad3731eeced25dce8c73 --- core/java/android/provider/Settings.java | 18 +- core/proto/android/providers/settings.proto | 4 +- core/res/res/values/strings.xml | 4 +- .../res/layout/zen_mode_duration_dialog.xml | 52 +++ packages/SettingsLib/res/values/strings.xml | 11 +- .../notification/ZenDurationDialog.java | 321 ++++++++++++++++++ .../notification/ZenDurationDialogTest.java | 222 ++++++++++++ .../SettingsProvider/res/values/defaults.xml | 7 + .../settings/SettingsProtoDumpUtil.java | 3 + .../providers/settings/SettingsProvider.java | 16 +- .../android/systemui/qs/tiles/DndTile.java | 34 +- proto/src/metrics_constants.proto | 1 - 12 files changed, 676 insertions(+), 17 deletions(-) create mode 100644 packages/SettingsLib/res/layout/zen_mode_duration_dialog.xml create mode 100644 packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java create mode 100644 packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bd29d2de7ba38..a14f8ef50669a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11167,6 +11167,20 @@ public final class Settings { */ public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag"; + /** + * If 0, turning on dnd manually will last indefinitely. + * Else if non-negative, turning on dnd manually will last for this many minutes. + * Else (if negative), turning on dnd manually will surface a dialog that prompts + * user to specify a duration. + * @hide + */ + public static final String ZEN_DURATION = "zen_duration"; + + private static final Validator ZEN_DURATION_VALIDATOR = ANY_INTEGER_VALIDATOR; + + /** @hide */ public static final int ZEN_DURATION_PROMPT = -1; + /** @hide */ public static final int ZEN_DURATION_FOREVER = 0; + /** * Defines global heads up toggle. One of HEADS_UP_OFF, HEADS_UP_ON. * @@ -11541,7 +11555,8 @@ public final class Settings { BLUETOOTH_ON, PRIVATE_DNS_MODE, PRIVATE_DNS_SPECIFIER, - SOFT_AP_TIMEOUT_ENABLED + SOFT_AP_TIMEOUT_ENABLED, + ZEN_DURATION, }; /** @@ -11580,6 +11595,7 @@ public final class Settings { VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON, WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR); VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR); + VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR); } /** diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto index d7ba421ec3c69..914a7db56f1d6 100644 --- a/core/proto/android/providers/settings.proto +++ b/core/proto/android/providers/settings.proto @@ -433,9 +433,11 @@ message GlobalSettingsProto { optional SettingProto show_mute_in_crash_dialog = 352 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingsProto show_zen_upgrade_notification = 354 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingsProto app_auto_restriction_enabled = 359 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingsProto zen_duration = 360 [ (android.privacy).dest = DEST_AUTOMATIC ]; + // Please insert fields in the same order as in // frameworks/base/core/java/android/provider/Settings.java. - // Next tag = 360; + // Next tag = 361; } message SecureSettingsProto { diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 15dffc0ba52c3..9fb47429612c9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4490,7 +4490,7 @@ - For one hour (until %2$s) + For 1 hour (until %2$s) For %1$d hours (until %2$s) @@ -4514,7 +4514,7 @@ - For one hour + For 1 hour For %d hours diff --git a/packages/SettingsLib/res/layout/zen_mode_duration_dialog.xml b/packages/SettingsLib/res/layout/zen_mode_duration_dialog.xml new file mode 100644 index 0000000000000..6552296bc4e4d --- /dev/null +++ b/packages/SettingsLib/res/layout/zen_mode_duration_dialog.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 275cbc09e753e..89af7f97ca557 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1062,10 +1062,13 @@ Less time. - - Turn on Cancel + + OK + + + Turn on Turn on Do Not Disturb @@ -1083,4 +1086,8 @@ on %1$s + + Duration + + Ask every time diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java new file mode 100644 index 0000000000000..7369ba8c7f1c8 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java @@ -0,0 +1,321 @@ +/* + * 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.settingslib.notification; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.provider.Settings; +import android.service.notification.Condition; +import android.service.notification.ZenModeConfig; +import android.support.annotation.VisibleForTesting; +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.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.policy.PhoneWindow; +import com.android.settingslib.R; + +import java.util.Arrays; + +public class ZenDurationDialog { + private static final int[] MINUTE_BUCKETS = ZenModeConfig.MINUTE_BUCKETS; + @VisibleForTesting protected static final int MIN_BUCKET_MINUTES = MINUTE_BUCKETS[0]; + @VisibleForTesting protected 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 protected int mBucketIndex = -1; + + @VisibleForTesting protected static final int FOREVER_CONDITION_INDEX = 0; + @VisibleForTesting protected static final int COUNTDOWN_CONDITION_INDEX = 1; + @VisibleForTesting protected static final int ALWAYS_ASK_CONDITION_INDEX = 2; + + @VisibleForTesting protected Context mContext; + @VisibleForTesting protected LinearLayout mZenRadioGroupContent; + private RadioGroup mZenRadioGroup; + private int MAX_MANUAL_DND_OPTIONS = 3; + + @VisibleForTesting protected LayoutInflater mLayoutInflater; + + public ZenDurationDialog(Context context) { + mContext = context; + } + + public Dialog createDialog() { + int zenDuration = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_FOREVER); + + final AlertDialog.Builder builder = new AlertDialog.Builder(mContext) + .setTitle(R.string.zen_mode_duration_settings_title) + .setNegativeButton(R.string.cancel, null) + .setPositiveButton(R.string.okay, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + updateZenDuration(zenDuration); + } + }); + + View contentView = getContentView(); + setupRadioButtons(zenDuration); + builder.setView(contentView); + return builder.create(); + } + + @VisibleForTesting + protected void updateZenDuration(int currZenDuration) { + final int checkedRadioButtonId = mZenRadioGroup.getCheckedRadioButtonId(); + + int newZenDuration = Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_FOREVER); + switch (checkedRadioButtonId) { + case FOREVER_CONDITION_INDEX: + newZenDuration = Settings.Global.ZEN_DURATION_FOREVER; + MetricsLogger.action(mContext, + MetricsProto.MetricsEvent. + NOTIFICATION_ZEN_MODE_DURATION_FOREVER); + break; + case COUNTDOWN_CONDITION_INDEX: + ConditionTag tag = getConditionTagAt(checkedRadioButtonId); + newZenDuration = tag.countdownZenDuration; + MetricsLogger.action(mContext, + MetricsProto.MetricsEvent. + NOTIFICATION_ZEN_MODE_DURATION_TIME, + newZenDuration); + break; + case ALWAYS_ASK_CONDITION_INDEX: + newZenDuration = Settings.Global.ZEN_DURATION_PROMPT; + MetricsLogger.action(mContext, + MetricsProto.MetricsEvent. + NOTIFICATION_ZEN_MODE_DURATION_PROMPT); + break; + } + + if (currZenDuration != newZenDuration) { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.ZEN_DURATION, newZenDuration); + } + } + + @VisibleForTesting + protected View getContentView() { + if (mLayoutInflater == null) { + mLayoutInflater = new PhoneWindow(mContext).getLayoutInflater(); + } + View contentView = mLayoutInflater.inflate(R.layout.zen_mode_duration_dialog, + null); + ScrollView container = (ScrollView) contentView.findViewById(R.id.zen_duration_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 = mLayoutInflater.inflate(R.layout.zen_mode_radio_button, + mZenRadioGroup, false); + mZenRadioGroup.addView(radioButton); + radioButton.setId(i); + + final View radioButtonContent = mLayoutInflater.inflate(R.layout.zen_mode_condition, + mZenRadioGroupContent, false); + radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS); + mZenRadioGroupContent.addView(radioButtonContent); + } + + return contentView; + } + + @VisibleForTesting + protected void setupRadioButtons(int zenDuration) { + int checkedIndex = ALWAYS_ASK_CONDITION_INDEX; + if (zenDuration == 0) { + checkedIndex = FOREVER_CONDITION_INDEX; + } else if (zenDuration > 0) { + checkedIndex = COUNTDOWN_CONDITION_INDEX; + } + + bindTag(zenDuration, mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX), + FOREVER_CONDITION_INDEX); + bindTag(zenDuration, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX), + COUNTDOWN_CONDITION_INDEX); + bindTag(zenDuration, mZenRadioGroupContent.getChildAt(ALWAYS_ASK_CONDITION_INDEX), + ALWAYS_ASK_CONDITION_INDEX); + getConditionTagAt(checkedIndex).rb.setChecked(true); + } + + private void bindTag(final int currZenDuration, final View row, final int rowIndex) { + final ConditionTag tag = row.getTag() != null ? (ConditionTag) row.getTag() : + new ConditionTag(); + row.setTag(tag); + + if (tag.rb == null) { + tag.rb = (RadioButton) mZenRadioGroup.getChildAt(rowIndex); + } + + // if duration is set to forever or always prompt, then countdown time defaults to 1 hour + if (currZenDuration <= 0) { + tag.countdownZenDuration = MINUTE_BUCKETS[DEFAULT_BUCKET_INDEX]; + } else { + tag.countdownZenDuration = currZenDuration; + } + + tag.rb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + tag.rb.setChecked(true); + } + } + }); + + updateUi(tag, row, rowIndex); + } + + @VisibleForTesting + protected ConditionTag getConditionTagAt(int index) { + return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag(); + } + + + private void setupUi(ConditionTag tag, View row) { + tag.lines = row.findViewById(android.R.id.content); + tag.line1 = (TextView) row.findViewById(android.R.id.text1); + + // text2 is not used in zen duration dialog + row.findViewById(android.R.id.text2).setVisibility(View.GONE); + + tag.lines.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + tag.rb.setChecked(true); + } + }); + } + + private void updateButtons(ConditionTag tag, View row, int rowIndex) { + // 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*/, rowIndex); + } + }); + + // 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*/, rowIndex); + } + }); + + final long time = tag.countdownZenDuration; + if (rowIndex == COUNTDOWN_CONDITION_INDEX) { + button1.setVisibility(View.VISIBLE); + button2.setVisibility(View.VISIBLE); + + button1.setEnabled(time > MIN_BUCKET_MINUTES); + button2.setEnabled(tag.countdownZenDuration != MAX_BUCKET_MINUTES); + + button1.setAlpha(button1.isEnabled() ? 1f : .5f); + button2.setAlpha(button2.isEnabled() ? 1f : .5f); + } else { + button1.setVisibility(View.GONE); + button2.setVisibility(View.GONE); + } + } + + @VisibleForTesting + protected void updateUi(ConditionTag tag, View row, int rowIndex) { + if (tag.lines == null) { + setupUi(tag, row); + } + + updateButtons(tag, row, rowIndex); + + String radioContentText = ""; + switch (rowIndex) { + case FOREVER_CONDITION_INDEX: + radioContentText = mContext.getString( + com.android.internal.R.string.zen_mode_forever); + break; + case COUNTDOWN_CONDITION_INDEX: + Condition condition = ZenModeConfig.toTimeCondition(mContext, + tag.countdownZenDuration, ActivityManager.getCurrentUser(), false); + radioContentText = condition.line1; + break; + case ALWAYS_ASK_CONDITION_INDEX: + radioContentText = mContext.getString( + R.string.zen_mode_duration_always_prompt_title); + break; + } + + tag.line1.setText(radioContentText); + } + + @VisibleForTesting + protected void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) { + int newDndTimeDuration = -1; + final int N = MINUTE_BUCKETS.length; + if (mBucketIndex == -1) { + // not on a known index, search for the next or prev bucket by time + final long time = tag.countdownZenDuration; + for (int i = 0; i < N; i++) { + int j = up ? i : N - 1 - i; + final int bucketMinutes = MINUTE_BUCKETS[j]; + if (up && bucketMinutes > time || !up && bucketMinutes < time) { + mBucketIndex = j; + newDndTimeDuration = bucketMinutes; + break; + } + } + if (newDndTimeDuration == -1) { + mBucketIndex = DEFAULT_BUCKET_INDEX; + newDndTimeDuration = MINUTE_BUCKETS[mBucketIndex]; + } + } else { + // on a known index, simply increment or decrement + mBucketIndex = Math.max(0, Math.min(N - 1, mBucketIndex + (up ? 1 : -1))); + newDndTimeDuration = MINUTE_BUCKETS[mBucketIndex]; + } + tag.countdownZenDuration = newDndTimeDuration; + bindTag(newDndTimeDuration, row, rowId); + tag.rb.setChecked(true); + } + + // used as the view tag on condition rows + @VisibleForTesting + protected static class ConditionTag { + public RadioButton rb; + public View lines; + public TextView line1; + public int countdownZenDuration; // only important for countdown radio button + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java new file mode 100644 index 0000000000000..9b491c200cdc0 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java @@ -0,0 +1,222 @@ +/* + * 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.settingslib.notification; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyObject; +import static org.mockito.ArgumentMatchers.anyInt; +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.AlertDialog; +import android.app.Fragment; +import android.app.NotificationManager; +import android.content.Context; +import android.content.ContentResolver; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.net.Uri; +import android.provider.Settings; +import android.service.notification.Condition; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +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; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class ZenDurationDialogTest { + private ZenDurationDialog mController; + + private Context mContext; + private LayoutInflater mLayoutInflater; + private Condition mCountdownCondition; + private Condition mAlarmCondition; + private ContentResolver mContentResolver; + + @Before + public void setup() { + mContext = RuntimeEnvironment.application; + mContentResolver = RuntimeEnvironment.application.getContentResolver(); + mLayoutInflater = LayoutInflater.from(mContext); + + mController = spy(new ZenDurationDialog(mContext)); + mController.mLayoutInflater = mLayoutInflater; + mController.getContentView(); + } + + @Test + public void testAlwaysPrompt() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_PROMPT); + mController.createDialog(); + + assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertTrue(mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked()); + } + + @Test + public void testForever() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_FOREVER); + mController.createDialog(); + + assertTrue(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked()); + } + + @Test + public void testSpecificDuration() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, 45); + mController.createDialog(); + + assertFalse(mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb + .isChecked()); + assertTrue(mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb + .isChecked()); + assertFalse(mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.isChecked()); + } + + + @Test + public void testChooseAlwaysPromptSetting() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_FOREVER); + + AlertDialog dialog = (AlertDialog) mController.createDialog(); + mController.getConditionTagAt(ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX).rb.setChecked( + true); + mController.updateZenDuration(Settings.Global.ZEN_DURATION_FOREVER); + + assertEquals(Settings.Global.ZEN_DURATION_PROMPT, Settings.Global.getInt(mContentResolver, + Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_FOREVER)); + } + + @Test + public void testChooseForeverSetting() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_PROMPT); + + AlertDialog dialog = (AlertDialog) mController.createDialog(); + mController.getConditionTagAt(ZenDurationDialog.FOREVER_CONDITION_INDEX).rb.setChecked( + true); + mController.updateZenDuration(Settings.Global.ZEN_DURATION_PROMPT); + + assertEquals(Settings.Global.ZEN_DURATION_FOREVER, Settings.Global.getInt(mContentResolver, + Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_PROMPT)); + } + + @Test + public void testChooseTimeSetting() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_PROMPT); + + AlertDialog dialog = (AlertDialog) mController.createDialog(); + mController.getConditionTagAt(ZenDurationDialog.COUNTDOWN_CONDITION_INDEX).rb.setChecked( + true); + mController.updateZenDuration(Settings.Global.ZEN_DURATION_PROMPT); + + // countdown defaults to 60 minutes: + assertEquals(60, Settings.Global.getInt(mContentResolver, + Settings.Global.ZEN_DURATION, Settings.Global.ZEN_DURATION_PROMPT)); + } + + @Test + public void testGetTimeFromBucket() { + Settings.Global.putInt(mContentResolver, Settings.Global.ZEN_DURATION, + Settings.Global.ZEN_DURATION_PROMPT); + + AlertDialog dialog = (AlertDialog) mController.createDialog(); + // click time button starts at 60 minutes + // - 1 hour to MAX_BUCKET_MINUTES (12 hours), increments by 1 hour + // - 0-60 minutes increments by 15 minutes + View view = mController.mZenRadioGroupContent.getChildAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + ZenDurationDialog.ConditionTag tag = mController.getConditionTagAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + + // test incrementing up: + mController.onClickTimeButton(view, tag, true, ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(120, tag.countdownZenDuration); // goes from 1 hour to 2 hours + + // try clicking up 50 times - should max out at ZenDurationDialog.MAX_BUCKET_MINUTES + for (int i = 0; i < 50; i++) { + mController.onClickTimeButton(view, tag, true, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + } + assertEquals(ZenDurationDialog.MAX_BUCKET_MINUTES, tag.countdownZenDuration); + + // reset, test incrementing down: + mController.mBucketIndex = -1; // reset current bucket index to reset countdownZenDuration + tag.countdownZenDuration = 60; // back to default + mController.onClickTimeButton(view, tag, false, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(45, tag.countdownZenDuration); // goes from 60 minutes to 45 minutes + + // try clicking down 50 times - should stop at MIN_BUCKET_MINUTES + for (int i = 0; i < 50; i++) { + mController.onClickTimeButton(view, tag, false, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + } + assertEquals(ZenDurationDialog.MIN_BUCKET_MINUTES, tag.countdownZenDuration); + + // reset countdownZenDuration to unbucketed number, should round change to nearest bucket + mController.mBucketIndex = -1; + tag.countdownZenDuration = 50; + mController.onClickTimeButton(view, tag, false, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(45, tag.countdownZenDuration); + + mController.mBucketIndex = -1; + tag.countdownZenDuration = 50; + mController.onClickTimeButton(view, tag, true, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(60, tag.countdownZenDuration); + + mController.mBucketIndex = -1; + tag.countdownZenDuration = 75; + mController.onClickTimeButton(view, tag, false, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(60, tag.countdownZenDuration); + + mController.mBucketIndex = -1; + tag.countdownZenDuration = 75; + mController.onClickTimeButton(view, tag, true, + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + assertEquals(120, tag.countdownZenDuration); + } +} \ No newline at end of file diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index c173225be5ce0..1cd02f418352e 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -200,4 +200,11 @@ + + + 0 diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 11c869f49ac43..ccb4d8c866da0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1141,6 +1141,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.APP_AUTO_RESTRICTION_ENABLED, GlobalSettingsProto.APP_AUTO_RESTRICTION_ENABLED); + dumpSetting(s, p, + Settings.Global.ZEN_DURATION, + GlobalSettingsProto.ZEN_DURATION); // Please insert new settings using the same order as in Settings.Global. } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index baf17cb0c463c..6398858479076 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2938,7 +2938,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 156; + private static final int SETTINGS_VERSION = 157; private final int mUserId; @@ -3604,7 +3604,21 @@ public class SettingsProvider extends ContentProvider { currentVersion = 156; } + if (currentVersion == 156) { + // Version 156: Set a default value for zen duration + final SettingsState globalSettings = getGlobalSettingsLocked(); + final Setting currentSetting = globalSettings.getSettingLocked( + Global.ZEN_DURATION); + if (currentSetting.isNull()) { + String defaultZenDuration = Integer.toString(getContext() + .getResources().getInteger(R.integer.def_zen_duration)); + globalSettings.insertSettingLocked( + Global.ZEN_DURATION, defaultZenDuration, + null, true, SettingsState.SYSTEM_PACKAGE_NAME); + } + currentVersion = 157; + } // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 2d31669db6c22..f3a2ae3770b6f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_OFF; +import android.app.ActivityManager; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -28,6 +29,7 @@ import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Global; @@ -139,15 +141,29 @@ public class DndTile extends QSTileImpl { @Override public void showDetail(boolean show) { - mUiHandler.post(() -> { - Dialog mDialog = new EnableZenModeDialog(mContext).createDialog(); - mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - SystemUIDialog.setShowForAllUsers(mDialog, true); - SystemUIDialog.registerDismissListener(mDialog); - SystemUIDialog.setWindowOnTop(mDialog); - mUiHandler.post(() -> mDialog.show()); - mHost.collapsePanels(); - }); + int zenDuration = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.ZEN_DURATION, 0); + switch (zenDuration) { + case Settings.Global.ZEN_DURATION_PROMPT: + mUiHandler.post(() -> { + Dialog mDialog = new EnableZenModeDialog(mContext).createDialog(); + mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + SystemUIDialog.setShowForAllUsers(mDialog, true); + SystemUIDialog.registerDismissListener(mDialog); + SystemUIDialog.setWindowOnTop(mDialog); + mUiHandler.post(() -> mDialog.show()); + mHost.collapsePanels(); + }); + break; + case Settings.Global.ZEN_DURATION_FOREVER: + mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG); + break; + default: + Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration, + ActivityManager.getCurrentUser(), true).id; + mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, + conditionId, TAG); + } } @Override diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index e962c0b23f98d..e7e765dcde917 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -5363,7 +5363,6 @@ message MetricsEvent { // OS: P ACTION_PANEL_VIEW_EXPAND = 1328; - // FIELD: Rotation of the device // CATEGORY: GLOBAL_SYSTEM_UI // OS: P