Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2dd7bde9 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Adding warning text to dnd dialog"

parents ad87a1ca e975a424
Loading
Loading
Loading
Loading
+34 −19
Original line number Diff line number Diff line
@@ -22,8 +22,13 @@
            android:fillViewport ="true"
            android:orientation="vertical">

    <LinearLayout
        android:id="@+id/dialog_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.android.settingslib.notification.ZenRadioLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/zen_conditions"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
@@ -43,4 +48,14 @@
                android:orientation="vertical"/>
        </com.android.settingslib.notification.ZenRadioLayout>

        <TextView
            android:id="@+id/zen_alarm_warning"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="18dp"
            android:layout_marginEnd="16dp"
            android:textDirection="locale"
            android:textColor="?android:attr/colorError"/>
    </LinearLayout>

</ScrollView>
 No newline at end of file
+8 −0
Original line number Diff line number Diff line
@@ -1039,5 +1039,13 @@
    <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>
    <!-- Warning text when an alarm might be silenced by Do Not Disturb [CHAR LIMIT=NONE] -->
    <string name="zen_alarm_warning_indef">You won\'t hear your next alarm <xliff:g id="when" example="at 7:00 AM">%1$s</xliff:g> unless you turn this off before then</string>
    <!-- Warning text when an alarm might be silenced by Do Not Disturb due to a time-based condition [CHAR LIMIT=NONE] -->
    <string name="zen_alarm_warning">You won\'t hear your next alarm <xliff:g id="when" example="at 7:00 AM">%1$s</xliff:g></string>
    <!-- Alarm template for near alarms [CHAR LIMIT=25] -->
    <string name="alarm_template">at <xliff:g id="when" example="7:00 AM">%1$s</xliff:g></string>
    <!-- Alarm template for far in the future alarms [CHAR LIMIT=25] -->
    <string name="alarm_template_far">on <xliff:g id="when" example="Fri 7:00 AM">%1$s</xliff:g></string>

</resources>
+89 −23
Original line number Diff line number Diff line
package com.android.settingslib.notification;

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
@@ -16,6 +14,8 @@ package com.android.settingslib.notification;
 * limitations under the License.
 */

package com.android.settingslib.notification;

import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlertDialog;
@@ -28,6 +28,7 @@ import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.Slog;
import android.view.LayoutInflater;
@@ -40,6 +41,7 @@ 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.internal.policy.PhoneWindow;
@@ -48,11 +50,11 @@ import com.android.settingslib.R;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;

public class EnableZenModeDialog {

    private static final String TAG = "QSEnableZenModeDialog";
    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;
@@ -60,25 +62,37 @@ public class EnableZenModeDialog {
    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);

    private static final int FOREVER_CONDITION_INDEX = 0;
    private static final int COUNTDOWN_CONDITION_INDEX = 1;
    private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
    @VisibleForTesting
    protected static final int FOREVER_CONDITION_INDEX = 0;
    @VisibleForTesting
    protected static final int COUNTDOWN_CONDITION_INDEX = 1;
    @VisibleForTesting
    protected static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;

    private static final int SECONDS_MS = 1000;
    private static final int MINUTES_MS = 60 * SECONDS_MS;

    private Uri mForeverId;
    @VisibleForTesting
    protected Uri mForeverId;
    private int mBucketIndex = -1;

    private AlarmManager mAlarmManager;
    private int mUserId;
    private boolean mAttached;

    private Context mContext;
    @VisibleForTesting
    protected Context mContext;
    @VisibleForTesting
    protected TextView mZenAlarmWarning;
    @VisibleForTesting
    protected LinearLayout mZenRadioGroupContent;

    private RadioGroup mZenRadioGroup;
    private LinearLayout mZenRadioGroupContent;
    private int MAX_MANUAL_DND_OPTIONS = 3;

    @VisibleForTesting
    protected LayoutInflater mLayoutInflater;

    public EnableZenModeDialog(Context context) {
        mContext = context;
    }
@@ -133,32 +147,40 @@ public class EnableZenModeDialog {
        for (int i = 0; i < N; i++) {
            mZenRadioGroupContent.getChildAt(i).setVisibility(View.GONE);
        }

        mZenAlarmWarning.setVisibility(View.GONE);
    }

    protected View getContentView() {
        final LayoutInflater inflater = new PhoneWindow(mContext).getLayoutInflater();
        View contentView = inflater.inflate(R.layout.zen_mode_turn_on_dialog_container, null);
        if (mLayoutInflater == null) {
            mLayoutInflater = new PhoneWindow(mContext).getLayoutInflater();
        }
        View contentView = mLayoutInflater.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);
        mZenAlarmWarning = container.findViewById(R.id.zen_alarm_warning);

        for (int i = 0; i < MAX_MANUAL_DND_OPTIONS; i++) {
            final View radioButton = inflater.inflate(R.layout.zen_mode_radio_button,
            final View radioButton = mLayoutInflater.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,
            final View radioButtonContent = mLayoutInflater.inflate(R.layout.zen_mode_condition,
                    mZenRadioGroupContent, false);
            radioButtonContent.setId(i + MAX_MANUAL_DND_OPTIONS);
            mZenRadioGroupContent.addView(radioButtonContent);
        }

        hideAllConditions();
        return contentView;
    }

    private void bind(final Condition condition, final View row, final int rowId) {
    @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() :
@@ -181,6 +203,7 @@ public class EnableZenModeDialog {
                    if (DEBUG) Log.d(TAG, "onCheckedChanged " + conditionId);
                    MetricsLogger.action(mContext,
                            MetricsProto.MetricsEvent.QS_DND_CONDITION_SELECT);
                    updateAlarmWarningText(tag.condition);
                    announceConditionSelection(tag);
                }
            }
@@ -190,11 +213,13 @@ public class EnableZenModeDialog {
        row.setVisibility(View.VISIBLE);
    }

    private ConditionTag getConditionTagAt(int index) {
    @VisibleForTesting
    protected ConditionTag getConditionTagAt(int index) {
        return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
    }

    private void bindConditions(Condition c) {
    @VisibleForTesting
    protected void bindConditions(Condition c) {
        // forever
        bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
                FOREVER_CONDITION_INDEX);
@@ -236,11 +261,13 @@ public class EnableZenModeDialog {
        return info != null ? info.getTriggerTime() : 0;
    }

    private boolean isAlarm(Condition c) {
    @VisibleForTesting
    protected boolean isAlarm(Condition c) {
        return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
    }

    private boolean isCountdown(Condition c) {
    @VisibleForTesting
    protected boolean isCountdown(Condition c) {
        return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
    }

@@ -264,7 +291,8 @@ public class EnableZenModeDialog {
    }

    // Returns a time condition if the next alarm is within the next week.
    private Condition getTimeUntilNextAlarmCondition() {
    @VisibleForTesting
    protected Condition getTimeUntilNextAlarmCondition() {
        GregorianCalendar weekRange = new GregorianCalendar();
        setToMidnight(weekRange);
        weekRange.add(Calendar.DATE, 6);
@@ -282,7 +310,8 @@ public class EnableZenModeDialog {
        return null;
    }

    private void bindGenericCountdown() {
    @VisibleForTesting
    protected void bindGenericCountdown() {
        mBucketIndex = DEFAULT_BUCKET_INDEX;
        Condition countdown = ZenModeConfig.toTimeCondition(mContext,
                MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
@@ -366,7 +395,8 @@ public class EnableZenModeDialog {
        }
    }

    private void bindNextAlarm(Condition c) {
    @VisibleForTesting
    protected void bindNextAlarm(Condition c) {
        View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
        ConditionTag tag = (ConditionTag) alarmContent.getTag();

@@ -415,6 +445,7 @@ public class EnableZenModeDialog {
                    MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
        }
        bind(newCondition, row, rowId);
        updateAlarmWarningText(tag.condition);
        tag.rb.setChecked(true);
        announceConditionSelection(tag);
    }
@@ -428,8 +459,43 @@ public class EnableZenModeDialog {
        }
    }

    private void updateAlarmWarningText(Condition condition) {
        String warningText = computeAlarmWarningText(condition);
        mZenAlarmWarning.setText(warningText);
        mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE);
    }

    private String computeAlarmWarningText(Condition condition) {
        final long now = System.currentTimeMillis();
        final long nextAlarm = getNextAlarm();
        if (nextAlarm < now) {
            return null;
        }
        int warningRes = 0;
        if (condition == null || isForever(condition)) {
            warningRes = R.string.zen_alarm_warning_indef;
        } else {
            final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
            if (time > now && nextAlarm < time) {
                warningRes = R.string.zen_alarm_warning;
            }
        }
        if (warningRes == 0) {
            return null;
        }
        final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
        final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
        final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
        final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
        final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
        final String template = mContext.getResources().getString(templateRes, formattedTime);
        return mContext.getResources().getString(warningRes, template);
    }

    // used as the view tag on condition rows
    private static class ConditionTag {
    @VisibleForTesting
    protected static class ConditionTag {
        public RadioButton rb;
        public View lines;
        public TextView line1;
+149 −0
Original line number Diff line number Diff line
/*
 * 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.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.settingslib.TestConfig;
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;
import org.robolectric.annotation.Config;

@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
public class EnableZenModeDialogTest {
    private EnableZenModeDialog mController;

    @Mock
    private Context mContext;
    @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(mContext.getApplicationContext()).thenReturn(mContext);
        when(mFragment.getContext()).thenReturn(mShadowContext);
        mLayoutInflater = LayoutInflater.from(mShadowContext);

        mController = spy(new EnableZenModeDialog(mContext));
        mController.mContext = mContext;
        mController.mLayoutInflater = mLayoutInflater;
        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();
        doReturn(0L).when(mController).getNextAlarm();
        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());
    }
}
 No newline at end of file