Merge "Show next alarm on ambient display"
This commit is contained in:
committed by
Android (Google) Code Review
commit
daa44c28d6
@@ -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(),
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user