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

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

Merge "Show next alarm on ambient display"

parents f4f9fb6d 1f7374a2
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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(),
+56 −16
Original line number Diff line number Diff line
@@ -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();
    }

        return builder.build();
    protected void addNextAlarm(ListBuilder builder) {
        if (TextUtils.isEmpty(mNextAlarm)) {
            return;
        }

        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 = "";
        }
        mContentResolver.notifyChange(mSliceUri, null /* observer */);
    }

    private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
        if (alarmClockInfo == null) {
            return false;
        }
        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();

        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 −2
Original line number Diff line number Diff line
@@ -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();
    }
}
+54 −10
Original line number Diff line number Diff line
@@ -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++;
        }
    }