OSDN Git Service

Require a more specific intent
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / keyguard / KeyguardSliceProvider.java
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16
17 package com.android.systemui.keyguard;
18
19 import android.app.ActivityManager;
20 import android.app.AlarmManager;
21 import android.app.NotificationManager;
22 import android.app.PendingIntent;
23 import android.content.BroadcastReceiver;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.graphics.drawable.Icon;
29 import android.icu.text.DateFormat;
30 import android.icu.text.DisplayContext;
31 import android.net.Uri;
32 import android.os.Handler;
33 import android.provider.Settings;
34 import android.service.notification.ZenModeConfig;
35 import android.text.TextUtils;
36
37 import com.android.internal.annotations.VisibleForTesting;
38 import com.android.systemui.R;
39 import com.android.systemui.statusbar.policy.NextAlarmController;
40 import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
41 import com.android.systemui.statusbar.policy.ZenModeController;
42 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
43
44 import java.util.Date;
45 import java.util.Locale;
46 import java.util.concurrent.TimeUnit;
47
48 import androidx.slice.Slice;
49 import androidx.slice.SliceProvider;
50 import androidx.slice.builders.ListBuilder;
51 import androidx.slice.builders.ListBuilder.RowBuilder;
52 import androidx.slice.builders.SliceAction;
53
54 /**
55  * Simple Slice provider that shows the current date.
56  */
57 public class KeyguardSliceProvider extends SliceProvider implements
58         NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback {
59
60     public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
61     public static final String KEYGUARD_DATE_URI = "content://com.android.systemui.keyguard/date";
62     public static final String KEYGUARD_NEXT_ALARM_URI =
63             "content://com.android.systemui.keyguard/alarm";
64     public static final String KEYGUARD_DND_URI = "content://com.android.systemui.keyguard/dnd";
65     public static final String KEYGUARD_ACTION_URI =
66             "content://com.android.systemui.keyguard/action";
67
68     /**
69      * Only show alarms that will ring within N hours.
70      */
71     @VisibleForTesting
72     static final int ALARM_VISIBILITY_HOURS = 12;
73
74     protected final Uri mSliceUri;
75     protected final Uri mDateUri;
76     protected final Uri mAlarmUri;
77     protected final Uri mDndUri;
78     private final Date mCurrentTime = new Date();
79     private final Handler mHandler;
80     private final AlarmManager.OnAlarmListener mUpdateNextAlarm = this::updateNextAlarm;
81     private ZenModeController mZenModeController;
82     private String mDatePattern;
83     private DateFormat mDateFormat;
84     private String mLastText;
85     private boolean mRegistered;
86     private String mNextAlarm;
87     private NextAlarmController mNextAlarmController;
88     protected AlarmManager mAlarmManager;
89     protected ContentResolver mContentResolver;
90     private AlarmManager.AlarmClockInfo mNextAlarmInfo;
91
92     /**
93      * Receiver responsible for time ticking and updating the date format.
94      */
95     @VisibleForTesting
96     final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
97         @Override
98         public void onReceive(Context context, Intent intent) {
99             final String action = intent.getAction();
100             if (Intent.ACTION_TIME_TICK.equals(action)
101                     || Intent.ACTION_DATE_CHANGED.equals(action)
102                     || Intent.ACTION_TIME_CHANGED.equals(action)
103                     || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
104                     || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
105                 if (Intent.ACTION_LOCALE_CHANGED.equals(action)
106                         || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
107                     // need to get a fresh date format
108                     mHandler.post(KeyguardSliceProvider.this::cleanDateFormat);
109                 }
110                 mHandler.post(KeyguardSliceProvider.this::updateClock);
111             }
112         }
113     };
114
115     public KeyguardSliceProvider() {
116         this(new Handler());
117     }
118
119     @VisibleForTesting
120     KeyguardSliceProvider(Handler handler) {
121         mHandler = handler;
122         mSliceUri = Uri.parse(KEYGUARD_SLICE_URI);
123         mDateUri = Uri.parse(KEYGUARD_DATE_URI);
124         mAlarmUri = Uri.parse(KEYGUARD_NEXT_ALARM_URI);
125         mDndUri = Uri.parse(KEYGUARD_DND_URI);
126     }
127
128     @Override
129     public Slice onBindSlice(Uri sliceUri) {
130         ListBuilder builder = new ListBuilder(getContext(), mSliceUri);
131         builder.addRow(new RowBuilder(builder, mDateUri).setTitle(mLastText));
132         addNextAlarm(builder);
133         addZenMode(builder);
134         addPrimaryAction(builder);
135         return builder.build();
136     }
137
138     protected void addPrimaryAction(ListBuilder builder) {
139         // Add simple action because API requires it; Keyguard handles presenting
140         // its own slices so this action + icon are actually never used.
141         PendingIntent pi = PendingIntent.getActivity(getContext(), 0,
142             new Intent(getContext(), KeyguardSliceProvider.class), 0);
143         Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
144         SliceAction action = new SliceAction(pi, icon, mLastText);
145
146         RowBuilder primaryActionRow = new RowBuilder(builder, Uri.parse(KEYGUARD_ACTION_URI))
147             .setPrimaryAction(action);
148         builder.addRow(primaryActionRow);
149     }
150
151     protected void addNextAlarm(ListBuilder builder) {
152         if (TextUtils.isEmpty(mNextAlarm)) {
153             return;
154         }
155
156         Icon alarmIcon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
157         RowBuilder alarmRowBuilder = new RowBuilder(builder, mAlarmUri)
158                 .setTitle(mNextAlarm)
159                 .addEndItem(alarmIcon);
160         builder.addRow(alarmRowBuilder);
161     }
162
163     /**
164      * Add zen mode (DND) icon to slice if it's enabled.
165      * @param builder The slice builder.
166      */
167     protected void addZenMode(ListBuilder builder) {
168         if (!isDndSuppressingNotifications()) {
169             return;
170         }
171         RowBuilder dndBuilder = new RowBuilder(builder, mDndUri)
172                 .setContentDescription(getContext().getResources()
173                         .getString(R.string.accessibility_quick_settings_dnd))
174                 .addEndItem(Icon.createWithResource(getContext(), R.drawable.stat_sys_dnd));
175         builder.addRow(dndBuilder);
176     }
177
178     /**
179      * Return true if DND is enabled suppressing notifications.
180      */
181     protected boolean isDndSuppressingNotifications() {
182         boolean suppressingNotifications = (mZenModeController.getConfig().suppressedVisualEffects
183                 & NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
184         return mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF
185                 && suppressingNotifications;
186     }
187
188     @Override
189     public boolean onCreateSliceProvider() {
190         mAlarmManager = getContext().getSystemService(AlarmManager.class);
191         mContentResolver = getContext().getContentResolver();
192         mNextAlarmController = new NextAlarmControllerImpl(getContext());
193         mNextAlarmController.addCallback(this);
194         mZenModeController = new ZenModeControllerImpl(getContext(), mHandler);
195         mZenModeController.addCallback(this);
196         mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern);
197         registerClockUpdate();
198         updateClock();
199         return true;
200     }
201
202     @Override
203     public void onZenChanged(int zen) {
204         mContentResolver.notifyChange(mSliceUri, null /* observer */);
205     }
206
207     @Override
208     public void onConfigChanged(ZenModeConfig config) {
209         mContentResolver.notifyChange(mSliceUri, null /* observer */);
210     }
211
212     private void updateNextAlarm() {
213         if (withinNHours(mNextAlarmInfo, ALARM_VISIBILITY_HOURS)) {
214             String pattern = android.text.format.DateFormat.is24HourFormat(getContext(),
215                     ActivityManager.getCurrentUser()) ? "H:mm" : "h:mm";
216             mNextAlarm = android.text.format.DateFormat.format(pattern,
217                     mNextAlarmInfo.getTriggerTime()).toString();
218         } else {
219             mNextAlarm = "";
220         }
221         mContentResolver.notifyChange(mSliceUri, null /* observer */);
222     }
223
224     private boolean withinNHours(AlarmManager.AlarmClockInfo alarmClockInfo, int hours) {
225         if (alarmClockInfo == null) {
226             return false;
227         }
228
229         long limit = System.currentTimeMillis() + TimeUnit.HOURS.toMillis(hours);
230         return mNextAlarmInfo.getTriggerTime() <= limit;
231     }
232
233     /**
234      * Registers a broadcast receiver for clock updates, include date, time zone and manually
235      * changing the date/time via the settings app.
236      */
237     private void registerClockUpdate() {
238         if (mRegistered) {
239             return;
240         }
241
242         IntentFilter filter = new IntentFilter();
243         filter.addAction(Intent.ACTION_DATE_CHANGED);
244         filter.addAction(Intent.ACTION_TIME_CHANGED);
245         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
246         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
247         getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
248                 null /* scheduler */);
249         mRegistered = true;
250     }
251
252     @VisibleForTesting
253     boolean isRegistered() {
254         return mRegistered;
255     }
256
257     protected void updateClock() {
258         final String text = getFormattedDate();
259         if (!text.equals(mLastText)) {
260             mLastText = text;
261             mContentResolver.notifyChange(mSliceUri, null /* observer */);
262         }
263     }
264
265     protected String getFormattedDate() {
266         if (mDateFormat == null) {
267             final Locale l = Locale.getDefault();
268             DateFormat format = DateFormat.getInstanceForSkeleton(mDatePattern, l);
269             format.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
270             mDateFormat = format;
271         }
272         mCurrentTime.setTime(System.currentTimeMillis());
273         return mDateFormat.format(mCurrentTime);
274     }
275
276     @VisibleForTesting
277     void cleanDateFormat() {
278         mDateFormat = null;
279     }
280
281     @Override
282     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
283         mNextAlarmInfo = nextAlarm;
284         mAlarmManager.cancel(mUpdateNextAlarm);
285
286         long triggerAt = mNextAlarmInfo == null ? -1 : mNextAlarmInfo.getTriggerTime()
287                 - TimeUnit.HOURS.toMillis(ALARM_VISIBILITY_HOURS);
288         if (triggerAt > 0) {
289             mAlarmManager.setExact(AlarmManager.RTC, triggerAt, "lock_screen_next_alarm",
290                     mUpdateNextAlarm, mHandler);
291         }
292         updateNextAlarm();
293     }
294 }