Merge "Update 'next alarm' tracking"

This commit is contained in:
Julia Reynolds
2017-12-04 20:24:03 +00:00
committed by Android (Google) Code Review
7 changed files with 805 additions and 59 deletions

View File

@@ -890,7 +890,17 @@ public class ZenModeConfig implements Parcelable {
}
public static boolean isValidScheduleConditionId(Uri conditionId) {
return tryParseScheduleConditionId(conditionId) != null;
ScheduleInfo info;
try {
info = tryParseScheduleConditionId(conditionId);
} catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
return false;
}
if (info == null || info.days == null || info.days.length == 0) {
return false;
}
return true;
}
public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {

View File

@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.systemui.HardwareUiLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -45,4 +45,4 @@
</LinearLayout>
</RelativeLayout>
</com.android.systemui.HardwareUiLayout>
</RelativeLayout>

View File

@@ -19,6 +19,9 @@ package com.android.systemui.volume;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
import static com.android.systemui.volume.Events.DISMISS_REASON_TOUCH_OUTSIDE;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -42,6 +45,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.util.Log;
import android.util.Slog;
@@ -67,6 +71,7 @@ import android.widget.TextView;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.HardwareBgDrawable;
import com.android.systemui.HardwareUiLayout;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -74,6 +79,7 @@ import com.android.systemui.plugins.VolumeDialog;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.VolumeDialogController.State;
import com.android.systemui.plugins.VolumeDialogController.StreamState;
import com.android.systemui.util.leak.RotationUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -97,9 +103,13 @@ public class VolumeDialogImpl implements VolumeDialog {
private final VolumeDialogController mController;
private Window mWindow;
private HardwareUiLayout mHardwareLayout;
//private HardwareUiLayout mHardwareLayout;
private CustomDialog mDialog;
private ViewGroup mDialogView;
private boolean mEdgeBleed;
private boolean mRoundedDivider;
private HardwareBgDrawable mBackground;
private int mRotation = ROTATION_NONE;
private ViewGroup mDialogRowsView;
private ViewGroup mDialogContentView;
private final List<VolumeRow> mRows = new ArrayList<>();
@@ -111,6 +121,8 @@ public class VolumeDialogImpl implements VolumeDialog {
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveSliderTint;
private final ColorStateList mInactiveSliderTint;
private static final String EDGE_BLEED = "sysui_hwui_edge_bleed";
private static final String ROUNDED_DIVIDER = "sysui_hwui_rounded_divider";
private boolean mShowing;
private boolean mShowA11yStream;
@@ -181,8 +193,16 @@ public class VolumeDialogImpl implements VolumeDialog {
return true;
}
});
mHardwareLayout = HardwareUiLayout.get(mDialogView);
mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mEdgeBleed = Settings.Secure.getInt(mContext.getContentResolver(),
EDGE_BLEED, 0) != 0;
mRoundedDivider = Settings.Secure.getInt(mContext.getContentResolver(),
ROUNDED_DIVIDER, 1) != 0;
updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, mContext);
mDialogView.setBackground(mBackground);
//mHardwareLayout = HardwareUiLayout.get(mDialogView);
//mHardwareLayout.setOutsideTouchListener(view -> dismiss(DISMISS_REASON_TOUCH_OUTSIDE));
mDialogContentView = mDialog.findViewById(R.id.volume_dialog_content);
mDialogRowsView = mDialogContentView.findViewById(R.id.volume_dialog_rows);
@@ -210,6 +230,25 @@ public class VolumeDialogImpl implements VolumeDialog {
updateRowsH(getActiveRow());
}
private int getEdgePadding() {
return mContext.getResources().getDimensionPixelSize(R.dimen.edge_margin);
}
private void updateEdgeMargin(int edge) {
if (mDialogView != null) {
mRotation = RotationUtils.getRotation(mContext);
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mDialogView.getLayoutParams();
if (mRotation == ROTATION_LANDSCAPE) {
params.topMargin = edge;
} else if (mRotation == ROTATION_SEASCAPE) {
params.bottomMargin = edge;
} else {
params.rightMargin = edge;
}
mDialogView.setLayoutParams(params);
}
}
private ColorStateList loadColorStateList(int colorResId) {
return ColorStateList.valueOf(mContext.getColor(colorResId));
}
@@ -389,11 +428,11 @@ public class VolumeDialogImpl implements VolumeDialog {
rescheduleTimeoutH();
if (mShowing) return;
mShowing = true;
mHardwareLayout.setTranslationX(getAnimTranslation());
mHardwareLayout.setAlpha(0);
mHardwareLayout.animate()
mDialogView.setTranslationY(getAnimTranslation());
mDialogView.setAlpha(0);
mDialogView.animate()
.alpha(1)
.translationX(0)
.translationY(0)
.setDuration(300)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.withEndAction(() -> {
@@ -432,9 +471,9 @@ public class VolumeDialogImpl implements VolumeDialog {
mHandler.removeMessages(H.SHOW);
if (!mShowing) return;
mShowing = false;
mHardwareLayout.setTranslationX(0);
mHardwareLayout.setAlpha(1);
mHardwareLayout.animate()
mDialogView.setTranslationX(0);
mDialogView.setAlpha(1);
mDialogView.animate()
.alpha(0)
.translationX(getAnimTranslation())
.setDuration(300)

View File

@@ -18,6 +18,7 @@ package com.android.server.notification;
import android.service.notification.ZenModeConfig.ScheduleInfo;
import android.util.ArraySet;
import android.util.Log;
import java.util.Calendar;
import java.util.Objects;
@@ -41,10 +42,25 @@ public class ScheduleCalendar {
}
public void maybeSetNextAlarm(long now, long nextAlarm) {
if (mSchedule != null) {
if (mSchedule.exitAtAlarm
&& (now > mSchedule.nextAlarm || nextAlarm < mSchedule.nextAlarm)) {
mSchedule.nextAlarm = nextAlarm;
if (mSchedule != null && mSchedule.exitAtAlarm) {
// alarm canceled
if (nextAlarm == 0) {
mSchedule.nextAlarm = 0;
}
// only allow alarms in the future
if (nextAlarm > now) {
// store earliest alarm
if (mSchedule.nextAlarm == 0) {
mSchedule.nextAlarm = nextAlarm;
} else {
mSchedule.nextAlarm = Math.min(mSchedule.nextAlarm, nextAlarm);
}
} else if (mSchedule.nextAlarm < now) {
if (ScheduleConditionProvider.DEBUG) {
Log.d(ScheduleConditionProvider.TAG,
"All alarms are in the past " + mSchedule.nextAlarm);
}
mSchedule.nextAlarm = 0;
}
}
}
@@ -87,6 +103,9 @@ public class ScheduleCalendar {
}
public boolean shouldExitForAlarm(long time) {
if (mSchedule == null) {
return false;
}
return mSchedule.exitAtAlarm
&& mSchedule.nextAlarm != 0
&& time >= mSchedule.nextAlarm;

View File

@@ -37,6 +37,8 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.notification.NotificationManagerService.DumpFilter;
import java.io.PrintWriter;
@@ -62,10 +64,9 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
private static final String SEPARATOR = ";";
private static final String SCP_SETTING = "snoozed_schedule_condition_provider";
private final Context mContext = this;
private final ArrayMap<Uri, ScheduleCalendar> mSubscriptions = new ArrayMap<>();
private ArraySet<Uri> mSnoozed = new ArraySet<>();
private ArraySet<Uri> mSnoozedForAlarm = new ArraySet<>();
private AlarmManager mAlarmManager;
private boolean mConnected;
@@ -102,7 +103,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
pw.println(mSubscriptions.get(conditionId).toString());
}
}
pw.println(" snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozed));
pw.println(" snoozed due to alarm: " + TextUtils.join(SEPARATOR, mSnoozedForAlarm));
dumpUpcomingTime(pw, "mNextAlarmTime", mNextAlarmTime, now);
}
@@ -129,7 +130,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
public void onSubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
if (!ZenModeConfig.isValidScheduleConditionId(conditionId)) {
notifyCondition(createCondition(conditionId, Condition.STATE_FALSE, "badCondition"));
notifyCondition(createCondition(conditionId, Condition.STATE_ERROR, "invalidId"));
return;
}
synchronized (mSubscriptions) {
@@ -169,32 +170,11 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
synchronized (mSubscriptions) {
setRegistered(!mSubscriptions.isEmpty());
for (Uri conditionId : mSubscriptions.keySet()) {
final ScheduleCalendar cal = mSubscriptions.get(conditionId);
if (cal != null && cal.isInSchedule(now)) {
if (conditionSnoozed(conditionId) || cal.shouldExitForAlarm(now)) {
conditionsToNotify.add(createCondition(
conditionId, Condition.STATE_FALSE, "alarmCanceled"));
addSnoozed(conditionId);
} else {
conditionsToNotify.add(createCondition(
conditionId, Condition.STATE_TRUE, "meetsSchedule"));
}
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
} else {
conditionsToNotify.add(createCondition(
conditionId, Condition.STATE_FALSE, "!meetsSchedule"));
removeSnoozed(conditionId);
if (cal != null && nextUserAlarmTime == 0) {
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
}
}
if (cal != null) {
final long nextChangeTime = cal.getNextChangeTime(now);
if (nextChangeTime > 0 && nextChangeTime > now) {
if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
mNextAlarmTime = nextChangeTime;
}
}
Condition condition =
evaluateSubscriptionLocked(conditionId, mSubscriptions.get(conditionId),
now, nextUserAlarmTime);
if (condition != null) {
conditionsToNotify.add(condition);
}
}
}
@@ -202,6 +182,39 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
updateAlarm(now, mNextAlarmTime);
}
@VisibleForTesting
@GuardedBy("mSubscriptions")
Condition evaluateSubscriptionLocked(Uri conditionId, ScheduleCalendar cal,
long now, long nextUserAlarmTime) {
Condition condition;
if (cal == null) {
condition = createCondition(conditionId, Condition.STATE_ERROR, "!invalidId");
removeSnoozed(conditionId);
return condition;
}
if (cal.isInSchedule(now)) {
if (conditionSnoozed(conditionId)) {
condition = createCondition(conditionId, Condition.STATE_FALSE, "snoozed");
} else if (cal.shouldExitForAlarm(now)) {
condition = createCondition(conditionId, Condition.STATE_FALSE, "alarmCanceled");
addSnoozed(conditionId);
} else {
condition = createCondition(conditionId, Condition.STATE_TRUE, "meetsSchedule");
}
} else {
condition = createCondition(conditionId, Condition.STATE_FALSE, "!meetsSchedule");
removeSnoozed(conditionId);
}
cal.maybeSetNextAlarm(now, nextUserAlarmTime);
final long nextChangeTime = cal.getNextChangeTime(now);
if (nextChangeTime > 0 && nextChangeTime > now) {
if (mNextAlarmTime == 0 || nextChangeTime < mNextAlarmTime) {
mNextAlarmTime = nextChangeTime;
}
}
return condition;
}
private void updateAlarm(long now, long time) {
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,
@@ -266,27 +279,28 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
}
private boolean conditionSnoozed(Uri conditionId) {
synchronized (mSnoozed) {
return mSnoozed.contains(conditionId);
synchronized (mSnoozedForAlarm) {
return mSnoozedForAlarm.contains(conditionId);
}
}
private void addSnoozed(Uri conditionId) {
synchronized (mSnoozed) {
mSnoozed.add(conditionId);
@VisibleForTesting
void addSnoozed(Uri conditionId) {
synchronized (mSnoozedForAlarm) {
mSnoozedForAlarm.add(conditionId);
saveSnoozedLocked();
}
}
private void removeSnoozed(Uri conditionId) {
synchronized (mSnoozed) {
mSnoozed.remove(conditionId);
synchronized (mSnoozedForAlarm) {
mSnoozedForAlarm.remove(conditionId);
saveSnoozedLocked();
}
}
public void saveSnoozedLocked() {
final String setting = TextUtils.join(SEPARATOR, mSnoozed);
private void saveSnoozedLocked() {
final String setting = TextUtils.join(SEPARATOR, mSnoozedForAlarm);
final int currentUser = ActivityManager.getCurrentUser();
Settings.Secure.putStringForUser(mContext.getContentResolver(),
SCP_SETTING,
@@ -294,8 +308,8 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
currentUser);
}
public void readSnoozed() {
synchronized (mSnoozed) {
private void readSnoozed() {
synchronized (mSnoozedForAlarm) {
long identity = Binder.clearCallingIdentity();
try {
final String setting = Settings.Secure.getStringForUser(
@@ -312,7 +326,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService {
if (TextUtils.isEmpty(token)) {
continue;
}
mSnoozed.add(Uri.parse(token));
mSnoozedForAlarm.add(Uri.parse(token));
}
}
} finally {

View File

@@ -0,0 +1,327 @@
/*
* 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.server.notification;
import static junit.framework.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.service.notification.ZenModeConfig;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Slog;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Calendar;
import java.util.GregorianCalendar;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class ScheduleCalendarTest extends NotificationTestCase {
private ScheduleCalendar mScheduleCalendar;
private ZenModeConfig.ScheduleInfo mScheduleInfo;
@Before
public void setUp() throws Exception {
mScheduleCalendar = new ScheduleCalendar();
mScheduleInfo = new ZenModeConfig.ScheduleInfo();
mScheduleInfo.days = new int[] {1, 2, 3, 4, 5};
mScheduleCalendar.setSchedule(mScheduleInfo);
}
@Test
public void testNullScheduleInfo() throws Exception {
mScheduleCalendar.setSchedule(null);
mScheduleCalendar.maybeSetNextAlarm(1000, 1999);
assertEquals(0, mScheduleCalendar.getNextChangeTime(1000));
assertFalse(mScheduleCalendar.isInSchedule(100));
assertFalse(mScheduleCalendar.shouldExitForAlarm(100));
}
@Test
public void testGetNextChangeTime_startToday() throws Exception {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 1);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay()};
mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) + 1;
mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
mScheduleCalendar.setSchedule(mScheduleInfo);
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
GregorianCalendar actual = new GregorianCalendar();
actual.setTimeInMillis(actualMs);
assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
actualMs);
}
@Test
public void testGetNextChangeTime_endToday() throws Exception {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 2);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay()};
mScheduleInfo.startHour = cal.get(Calendar.HOUR_OF_DAY) - 1;
mScheduleInfo.endHour = cal.get(Calendar.HOUR_OF_DAY) + 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour);
expected.set(Calendar.MINUTE, mScheduleInfo.endMinute);
long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
GregorianCalendar actual = new GregorianCalendar();
actual.setTimeInMillis(actualMs);
assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
actualMs);
}
@Test
public void testGetNextChangeTime_startTomorrow() throws Exception {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
mScheduleInfo.startHour = 1;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
expected.add(Calendar.DATE, 1);
expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.startHour);
expected.set(Calendar.MINUTE, mScheduleInfo.startMinute);
long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
GregorianCalendar actual = new GregorianCalendar();
actual.setTimeInMillis(actualMs);
assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
actualMs);
}
@Test
public void testGetNextChangeTime_endTomorrow() throws Exception {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay(), getTodayDay() + 1};
mScheduleInfo.startHour = 22;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
mScheduleInfo.exitAtAlarm = false;
Calendar expected = new GregorianCalendar();
expected.setTimeInMillis(cal.getTimeInMillis());
expected.add(Calendar.DATE, 1);
expected.set(Calendar.HOUR_OF_DAY, mScheduleInfo.endHour);
expected.set(Calendar.MINUTE, mScheduleInfo.endMinute);
long actualMs = mScheduleCalendar.getNextChangeTime(cal.getTimeInMillis());
GregorianCalendar actual = new GregorianCalendar();
actual.setTimeInMillis(actualMs);
assertEquals("Expected " + expected + " was " + actual, expected.getTimeInMillis(),
actualMs);
}
@Test
public void testShouldExitForAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 1000;
assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
}
@Test
public void testShouldExitForAlarm_beforeAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@Test
public void testShouldExitForAlarm_noAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
assertFalse(mScheduleCalendar.shouldExitForAlarm(999));
}
@Test
public void testShouldExitForAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 1000;
assertTrue(mScheduleCalendar.shouldExitForAlarm(1000));
}
@Test
public void testMaybeSetNextAlarm_settingOff() {
mScheduleInfo.exitAtAlarm = false;
mScheduleInfo.nextAlarm = 0;
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
assertEquals(0, mScheduleInfo.nextAlarm);
}
@Test
public void testMaybeSetNextAlarm_settingOn() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 0;
mScheduleCalendar.maybeSetNextAlarm(1000, 2000);
assertEquals(2000, mScheduleInfo.nextAlarm);
}
@Test
public void testMaybeSetNextAlarm_alarmCanceled() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 10000;
mScheduleCalendar.maybeSetNextAlarm(1000, 0);
assertEquals(0, mScheduleInfo.nextAlarm);
}
@Test
public void testMaybeSetNextAlarm_earlierAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 2000;
mScheduleCalendar.maybeSetNextAlarm(1000, 1500);
assertEquals(1500, mScheduleInfo.nextAlarm);
}
@Test
public void testMaybeSetNextAlarm_laterAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 2000;
mScheduleCalendar.maybeSetNextAlarm(1000, 3000);
assertEquals(2000, mScheduleInfo.nextAlarm);
}
@Test
public void testMaybeSetNextAlarm_expiredAlarm() {
mScheduleInfo.exitAtAlarm = true;
mScheduleInfo.nextAlarm = 998;
mScheduleCalendar.maybeSetNextAlarm(1000, 999);
assertEquals(0, mScheduleInfo.nextAlarm);
}
@Test
public void testIsInSchedule_inScheduleOvernight() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay()};
mScheduleInfo.startHour = 22;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 15;
mScheduleInfo.endMinute = 15;
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@Test
public void testIsInSchedule_inScheduleSingleDay() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 14);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay()};
mScheduleInfo.startHour = 12;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@Test
public void testIsInSchedule_notToday() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 14);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
mScheduleInfo.days = new int[] {Calendar.FRIDAY, Calendar.SUNDAY};
mScheduleInfo.startHour = 12;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endHour = 15;
mScheduleInfo.endMinute = 15;
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
@Test
public void testIsInSchedule_startingSoon() {
Calendar cal = new GregorianCalendar();
cal.set(Calendar.HOUR_OF_DAY, 14);
cal.set(Calendar.MINUTE, 15);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 0);
mScheduleInfo.days = new int[] {getTodayDay()};
mScheduleInfo.startHour = 14;
mScheduleInfo.endHour = 3;
mScheduleInfo.startMinute = 16;
mScheduleInfo.endMinute = 15;
assertFalse(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
}
private int getTodayDay() {
return new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
}
}

View File

@@ -0,0 +1,337 @@
package com.android.server.notification;
import static org.mockito.Mockito.spy;
import android.content.Intent;
import android.net.Uri;
import android.os.Looper;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.support.test.InstrumentationRegistry;
import android.test.ServiceTestCase;
import android.testing.TestableContext;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.MockitoAnnotations;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class ScheduleConditionProviderTest extends ServiceTestCase<ScheduleConditionProvider> {
ScheduleConditionProvider mService;
@Rule
public final TestableContext mContext =
new TestableContext(InstrumentationRegistry.getContext(), null);
public ScheduleConditionProviderTest() {
super(ScheduleConditionProvider.class);
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Looper.prepare();
Intent startIntent =
new Intent("com.android.server.notification.ScheduleConditionProvider");
startIntent.setPackage("android");
bindService(startIntent);
mService = spy(getService());
}
@Test
public void testIsValidConditionId_incomplete() throws Exception {
Uri badConditionId = Uri.EMPTY;
assertFalse(mService.isValidConditionId(badConditionId));
assertEquals(Condition.STATE_ERROR,
mService.evaluateSubscriptionLocked(badConditionId, null, 0, 1000).state);
}
@Test
public void testIsValidConditionId() throws Exception {
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {1, 2, 4};
info.startHour = 8;
info.startMinute = 56;
info.nextAlarm = 1000;
info.exitAtAlarm = true;
info.endHour = 12;
info.endMinute = 9;
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
assertTrue(mService.isValidConditionId(conditionId));
}
@Test
public void testEvaluateSubscription_noAlarmExit_InSchedule() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {Calendar.FRIDAY};
info.startHour = now.get(Calendar.HOUR_OF_DAY);
info.startMinute = now.get(Calendar.MINUTE);
info.nextAlarm = 0;
info.exitAtAlarm = false;
info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
info.endMinute = info.startMinute;
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
assertTrue(cal.isInSchedule(now.getTimeInMillis()));
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
}
@Test
public void testEvaluateSubscription_noAlarmExit_InScheduleSnoozed() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {Calendar.FRIDAY};
info.startHour = now.get(Calendar.HOUR_OF_DAY);
info.startMinute = now.get(Calendar.MINUTE);
info.nextAlarm = 0;
info.exitAtAlarm = false;
info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
info.endMinute = info.startMinute;
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
assertTrue(cal.isInSchedule(now.getTimeInMillis()));
mService.addSnoozed(conditionId);
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_noAlarmExit_beforeSchedule() {
Calendar now = new GregorianCalendar();
now.set(Calendar.HOUR_OF_DAY, 14);
now.set(Calendar.MINUTE, 15);
now.set(Calendar.SECOND, 59);
now.set(Calendar.MILLISECOND, 0);
now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
// Schedule - 1 hour long; starts in 1 second
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {Calendar.FRIDAY};
info.startHour = now.get(Calendar.HOUR_OF_DAY);
info.startMinute = now.get(Calendar.MINUTE) + 1;
info.nextAlarm = 0;
info.exitAtAlarm = false;
info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
info.endMinute = info.startMinute;
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_noAlarmExit_endSchedule() {
Calendar now = getNow();
// Schedule - 1 hour long; ends now
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {Calendar.FRIDAY};
info.startHour = now.get(Calendar.HOUR_OF_DAY) - 1;
info.startMinute = now.get(Calendar.MINUTE);
info.nextAlarm = 0;
info.exitAtAlarm = false;
info.endHour = now.get(Calendar.HOUR_OF_DAY);
info.endMinute = now.get(Calendar.MINUTE);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_alarmSetBeforeInSchedule() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now, ends with alarm
ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
// an hour before start, update with an alarm that will fire during the schedule
mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() - 1000, now.getTimeInMillis() + 1000);
// at start, should be in dnd
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
// at alarm fire time, should exit dnd
assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 1000, 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_alarmSetInSchedule() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now, ends with alarm
ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
// at start, should be in dnd
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), 0);
assertEquals(Condition.STATE_TRUE, condition.state);
// in schedule, update with alarm time, should be in dnd
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
// at alarm fire time, should exit dnd
assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 1000, 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_earlierAlarmSet() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now, ends with alarm
ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
// at start, should be in dnd, alarm in 2000 ms
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 2000);
assertEquals(Condition.STATE_TRUE, condition.state);
// in schedule, update with earlier alarm time, should be in dnd
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
// at earliest alarm fire time, should exit dnd
assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000));
assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
cal.shouldExitForAlarm(now.getTimeInMillis() + 1000));
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 1000, 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_laterAlarmSet() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now, ends with alarm
ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
// at start, should be in dnd, alarm in 500 ms
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500);
assertEquals(Condition.STATE_TRUE, condition.state);
// in schedule, update with later alarm time, should be in dnd
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 250, now.getTimeInMillis() + 1000);
assertEquals(Condition.STATE_TRUE, condition.state);
// at earliest alarm fire time, should exit dnd
assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500));
assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(),
cal.shouldExitForAlarm(now.getTimeInMillis() + 500));
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 500, 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
@Test
public void testEvaluateSubscription_alarmCanceled() {
Calendar now = getNow();
// Schedule - 1 hour long; starts now, ends with alarm
ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now);
Uri conditionId = ZenModeConfig.toScheduleConditionId(info);
ScheduleCalendar cal = new ScheduleCalendar();
cal.setSchedule(info);
// at start, should be in dnd, alarm in 500 ms
Condition condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500);
assertEquals(Condition.STATE_TRUE, condition.state);
// in schedule, cancel alarm
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 250, 0);
assertEquals(Condition.STATE_TRUE, condition.state);
// at previous alarm time, should not exit DND
assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500));
assertFalse(cal.shouldExitForAlarm(now.getTimeInMillis() + 500));
condition = mService.evaluateSubscriptionLocked(
conditionId, cal, now.getTimeInMillis() + 500, 0);
assertEquals(Condition.STATE_TRUE, condition.state);
// end of schedule, exit DND
now.add(Calendar.HOUR_OF_DAY, 1);
condition = mService.evaluateSubscriptionLocked(conditionId, cal, now.getTimeInMillis(), 0);
assertEquals(Condition.STATE_FALSE, condition.state);
}
private Calendar getNow() {
Calendar now = new GregorianCalendar();
now.set(Calendar.HOUR_OF_DAY, 14);
now.set(Calendar.MINUTE, 16);
now.set(Calendar.SECOND, 0);
now.set(Calendar.MILLISECOND, 0);
now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
return now;
}
private ZenModeConfig.ScheduleInfo getScheduleEndsInHour(Calendar now) {
ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo();
info.days = new int[] {Calendar.FRIDAY};
info.startHour = now.get(Calendar.HOUR_OF_DAY);
info.startMinute = now.get(Calendar.MINUTE);
info.exitAtAlarm = true;
info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1;
info.endMinute = now.get(Calendar.MINUTE);
return info;
}
}