diff options
| author | 2018-02-26 18:08:33 -0800 | |
|---|---|---|
| committer | 2018-02-27 14:35:44 -0800 | |
| commit | 1f7374a276c722673951abb3fba897900b61d08d (patch) | |
| tree | 47a0d62c76b973f4e55c8d60291575541bb39d25 | |
| parent | e7522dd5461c32aee0be3afb1dc2c5883cc3a4b2 (diff) | |
Show next alarm on ambient display
Next alarm will be visible 12h before triggering.
Test: Set alarm that will ring in 8h
Test: Set alarm that will ring in 14h
Test: Set alarm that will ring in 11:59, wait one minute
Test: atest packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
Change-Id: Icd4253771efcdf5afb4e9e52329fa410d7fd1cc1
4 files changed, 127 insertions, 29 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index b54d09a66535..2adb2869e0f6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -167,7 +167,8 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe } mClickActions.put(button, pendingIntent); - button.setText(rc.getTitleItem().getText()); + final SliceItem titleItem = rc.getTitleItem(); + button.setText(titleItem == null ? null : titleItem.getText()); Drawable iconDrawable = null; SliceItem icon = SliceQuery.find(item.getSlice(), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index c7d276c1b7a3..26618bff4db7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -19,6 +19,7 @@ package com.android.systemui.keyguard; import android.app.ActivityManager; import android.app.AlarmManager; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -27,6 +28,7 @@ import android.icu.text.DateFormat; import android.icu.text.DisplayContext; import android.net.Uri; import android.os.Handler; +import android.os.SystemClock; import android.text.TextUtils; import com.android.internal.annotations.VisibleForTesting; @@ -36,6 +38,7 @@ import com.android.systemui.statusbar.policy.NextAlarmControllerImpl; import java.util.Date; import java.util.Locale; +import java.util.concurrent.TimeUnit; import androidx.app.slice.Slice; import androidx.app.slice.SliceProvider; @@ -53,6 +56,12 @@ public class KeyguardSliceProvider extends SliceProvider implements public static final String KEYGUARD_NEXT_ALARM_URI = "content://com.android.systemui.keyguard/alarm"; + /** + * Only show alarms that will ring within N hours. + */ + @VisibleForTesting + static final int ALARM_VISIBILITY_HOURS = 12; + private final Date mCurrentTime = new Date(); protected final Uri mSliceUri; protected final Uri mDateUri; @@ -65,6 +74,10 @@ public class KeyguardSliceProvider extends SliceProvider implements private boolean mRegisteredEveryMinute; private String mNextAlarm; private NextAlarmController mNextAlarmController; + protected AlarmManager mAlarmManager; + protected ContentResolver mContentResolver; + private AlarmManager.AlarmClockInfo mNextAlarmInfo; + private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm; /** * Receiver responsible for time ticking and updating the date format. @@ -105,17 +118,26 @@ public class KeyguardSliceProvider extends SliceProvider implements public Slice onBindSlice(Uri sliceUri) { ListBuilder builder = new ListBuilder(getContext(), mSliceUri); builder.addRow(new RowBuilder(builder, mDateUri).setTitle(mLastText)); - if (!TextUtils.isEmpty(mNextAlarm)) { - Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big); - builder.addRow(new RowBuilder(builder, mAlarmUri) - .setTitle(mNextAlarm).addEndItem(icon)); + addNextAlarm(builder); + return builder.build(); + } + + protected void addNextAlarm(ListBuilder builder) { + if (TextUtils.isEmpty(mNextAlarm)) { + return; } - return builder.build(); + Icon alarmIcon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big); + RowBuilder alarmRowBuilder = new RowBuilder(builder, mAlarmUri) + .setTitle(mNextAlarm) + .addEndItem(alarmIcon); + builder.addRow(alarmRowBuilder); } @Override public boolean onCreateSliceProvider() { + mAlarmManager = getContext().getSystemService(AlarmManager.class); + mContentResolver = getContext().getContentResolver(); mNextAlarmController = new NextAlarmControllerImpl(getContext()); mNextAlarmController.addCallback(this); mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern); @@ -124,15 +146,25 @@ public class KeyguardSliceProvider extends SliceProvider implements return true; } - public static String formatNextAlarm(Context context, AlarmManager.AlarmClockInfo info) { - if (info == null) { - return ""; + private void updateNextAlarm() { + if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) { + String pattern = android.text.format.DateFormat.is24HourFormat(getContext(), + ActivityManager.getCurrentUser()) ? "H:mm" : "h:mm"; + mNextAlarm = android.text.format.DateFormat.format(pattern, + mNextAlarmInfo.getTriggerTime()).toString(); + } else { + mNextAlarm = ""; } - String skeleton = android.text.format.DateFormat - .is24HourFormat(context, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma"; - String pattern = android.text.format.DateFormat - .getBestDateTimePattern(Locale.getDefault(), skeleton); - return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString(); + mContentResolver.notifyChange(mSliceUri, null /* observer */); + } + + private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) { + if (alarmClockInfo == null) { + return false; + } + + long limit = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(hours); + return mNextAlarmInfo.getTriggerTime() <= limit; } /** @@ -181,7 +213,7 @@ public class KeyguardSliceProvider extends SliceProvider implements final String text = getFormattedDate(); if (!text.equals(mLastText)) { mLastText = text; - getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */); + mContentResolver.notifyChange(mSliceUri, null /* observer */); } } @@ -203,7 +235,15 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { - mNextAlarm = formatNextAlarm(getContext(), nextAlarm); - getContext().getContentResolver().notifyChange(mSliceUri, null /* observer */); + mNextAlarmInfo = nextAlarm; + mAlarmManager.cancel(mUpdateNextAlarm); + + long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime() + - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS); + if (triggerAt > 0) { + mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm", + mUpdateNextAlarm, mHandler); + } + updateNextAlarm(); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 78481d3b07e0..2151436e9dd6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -15,10 +15,10 @@ package com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; -import static com.android.systemui.keyguard.KeyguardSliceProvider.formatNextAlarm; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.app.ActivityManager; import android.app.AlarmManager; import android.content.Context; import android.content.Intent; @@ -51,6 +51,8 @@ import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.NextAlarmController; +import java.util.Locale; + /** * View that contains the top-most bits of the screen (primarily the status bar with date, time, and * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner @@ -289,7 +291,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue @Override public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { - mNextAlarmText = nextAlarm != null ? formatNextAlarm(mContext, nextAlarm) : null; + mNextAlarmText = nextAlarm != null ? formatNextAlarm(nextAlarm) : null; if (mNextAlarmText != null) { hideLongPressTooltip(true /* shouldFadeInAlarmText */); } else { @@ -430,4 +432,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue public void setCallback(Callback qsPanelCallback) { mHeaderQsPanel.setCallback(qsPanelCallback); } + + private String formatNextAlarm(AlarmManager.AlarmClockInfo info) { + if (info == null) { + return ""; + } + String skeleton = android.text.format.DateFormat + .is24HourFormat(mContext, ActivityManager.getCurrentUser()) ? "EHm" : "Ehma"; + String pattern = android.text.format.DateFormat + .getBestDateTimePattern(Locale.getDefault(), skeleton); + return android.text.format.DateFormat.format(pattern, info.getTriggerTime()).toString(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index cd409d86a3dc..b6116e00bac1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -16,10 +16,17 @@ package com.android.systemui.keyguard; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + import androidx.app.slice.Slice; + +import android.app.AlarmManager; +import android.content.ContentResolver; import android.content.Intent; import android.net.Uri; -import android.os.Debug; import android.os.Handler; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -32,24 +39,31 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.Arrays; +import java.util.concurrent.TimeUnit; import androidx.app.slice.SliceItem; import androidx.app.slice.SliceProvider; import androidx.app.slice.SliceSpecs; import androidx.app.slice.core.SliceQuery; -import androidx.app.slice.widget.SliceLiveData; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) public class KeyguardSliceProviderTest extends SysuiTestCase { + @Mock + private ContentResolver mContentResolver; + @Mock + private AlarmManager mAlarmManager; private TestableKeyguardSliceProvider mProvider; @Before public void setup() { + MockitoAnnotations.initMocks(this); mProvider = new TestableKeyguardSliceProvider(); mProvider.attachInfo(getContext(), null); SliceProvider.setSpecs(Arrays.asList(SliceSpecs.LIST)); @@ -70,7 +84,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void returnsValidSlice() { - Slice slice = mProvider.onBindSlice(Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI)); + Slice slice = mProvider.onBindSlice(mProvider.getUri()); SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT, android.app.slice.Slice.HINT_TITLE, null /* nonHints */); @@ -87,21 +101,52 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void updatesClock() { - mProvider.mUpdateClockInvokations = 0; mProvider.mIntentReceiver.onReceive(getContext(), new Intent(Intent.ACTION_TIME_TICK)); TestableLooper.get(this).processAllMessages(); - Assert.assertEquals("Clock should have been updated.", 1 /* expected */, - mProvider.mUpdateClockInvokations); + verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); + } + + @Test + public void schedulesAlarm12hBefore() { + long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(16); + AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null); + mProvider.onNextAlarmChanged(alarmClockInfo); + + long twelveHours = TimeUnit.HOURS.toMillis(KeyguardSliceProvider.ALARM_VISIBILITY_HOURS); + long triggerAt = in16Hours - twelveHours; + verify(mAlarmManager).setExact(eq(AlarmManager.RTC), eq(triggerAt), anyString(), any(), + any()); + } + + @Test + public void updatingNextAlarmInvalidatesSlice() { + long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(8); + AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null); + mProvider.onNextAlarmChanged(alarmClockInfo); + + verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); } private class TestableKeyguardSliceProvider extends KeyguardSliceProvider { int mCleanDateFormatInvokations; - int mUpdateClockInvokations; + private int mCounter; TestableKeyguardSliceProvider() { super(new Handler(TestableLooper.get(KeyguardSliceProviderTest.this).getLooper())); } + Uri getUri() { + return mSliceUri; + } + + @Override + public boolean onCreateSliceProvider() { + super.onCreateSliceProvider(); + mAlarmManager = KeyguardSliceProviderTest.this.mAlarmManager; + mContentResolver = KeyguardSliceProviderTest.this.mContentResolver; + return true; + } + @Override void cleanDateFormat() { super.cleanDateFormat(); @@ -109,9 +154,8 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { } @Override - protected void updateClock() { - super.updateClock(); - mUpdateClockInvokations++; + protected String getFormattedDate() { + return super.getFormattedDate() + mCounter++; } } |