OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / core / java / com / android / internal / app / NightDisplayController.java
1 /*
2  * Copyright (C) 2016 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.internal.app;
18
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.database.ContentObserver;
24 import android.net.Uri;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.os.UserHandle;
28 import android.provider.Settings.Secure;
29 import android.util.Slog;
30
31 import com.android.internal.R;
32
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Calendar;
36 import java.util.Locale;
37
38 /**
39  * Controller for managing Night display settings.
40  * <p/>
41  * Night display tints your screen red at night. This makes it easier to look at your screen in
42  * dim light and may help you fall asleep more easily.
43  */
44 public final class NightDisplayController {
45
46     private static final String TAG = "NightDisplayController";
47     private static final boolean DEBUG = false;
48
49     /** @hide */
50     @Retention(RetentionPolicy.SOURCE)
51     @IntDef({ AUTO_MODE_DISABLED, AUTO_MODE_CUSTOM, AUTO_MODE_TWILIGHT })
52     public @interface AutoMode {}
53
54     /**
55      * Auto mode value to prevent Night display from being automatically activated. It can still
56      * be activated manually via {@link #setActivated(boolean)}.
57      *
58      * @see #setAutoMode(int)
59      */
60     public static final int AUTO_MODE_DISABLED = 0;
61     /**
62      * Auto mode value to automatically activate Night display at a specific start and end time.
63      *
64      * @see #setAutoMode(int)
65      * @see #setCustomStartTime(LocalTime)
66      * @see #setCustomEndTime(LocalTime)
67      */
68     public static final int AUTO_MODE_CUSTOM = 1;
69     /**
70      * Auto mode value to automatically activate Night display from sunset to sunrise.
71      *
72      * @see #setAutoMode(int)
73      */
74     public static final int AUTO_MODE_TWILIGHT = 2;
75
76     private final Context mContext;
77     private final int mUserId;
78
79     private final ContentObserver mContentObserver;
80
81     private Callback mCallback;
82
83     public NightDisplayController(@NonNull Context context) {
84         this(context, UserHandle.myUserId());
85     }
86
87     public NightDisplayController(@NonNull Context context, int userId) {
88         mContext = context.getApplicationContext();
89         mUserId = userId;
90
91         mContentObserver = new ContentObserver(new Handler(Looper.getMainLooper())) {
92             @Override
93             public void onChange(boolean selfChange, Uri uri) {
94                 super.onChange(selfChange, uri);
95
96                 final String setting = uri == null ? null : uri.getLastPathSegment();
97                 if (setting != null) {
98                     onSettingChanged(setting);
99                 }
100             }
101         };
102     }
103
104     /**
105      * Returns {@code true} when Night display is activated (the display is tinted red).
106      */
107     public boolean isActivated() {
108         return Secure.getIntForUser(mContext.getContentResolver(),
109                 Secure.NIGHT_DISPLAY_ACTIVATED, 0, mUserId) == 1;
110     }
111
112     /**
113      * Sets whether Night display should be activated.
114      *
115      * @param activated {@code true} if Night display should be activated
116      * @return {@code true} if the activated value was set successfully
117      */
118     public boolean setActivated(boolean activated) {
119         return Secure.putIntForUser(mContext.getContentResolver(),
120                 Secure.NIGHT_DISPLAY_ACTIVATED, activated ? 1 : 0, mUserId);
121     }
122
123     /**
124      * Returns the current auto mode value controlling when Night display will be automatically
125      * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
126      * {@link #AUTO_MODE_TWILIGHT}.
127      */
128     public @AutoMode int getAutoMode() {
129         int autoMode = Secure.getIntForUser(mContext.getContentResolver(),
130                 Secure.NIGHT_DISPLAY_AUTO_MODE, -1, mUserId);
131         if (autoMode == -1) {
132             if (DEBUG) {
133                 Slog.d(TAG, "Using default value for setting: " + Secure.NIGHT_DISPLAY_AUTO_MODE);
134             }
135             autoMode = mContext.getResources().getInteger(
136                     R.integer.config_defaultNightDisplayAutoMode);
137         }
138
139         if (autoMode != AUTO_MODE_DISABLED
140                 && autoMode != AUTO_MODE_CUSTOM
141                 && autoMode != AUTO_MODE_TWILIGHT) {
142             Slog.e(TAG, "Invalid autoMode: " + autoMode);
143             autoMode = AUTO_MODE_DISABLED;
144         }
145
146         return autoMode;
147     }
148
149     /**
150      * Sets the current auto mode value controlling when Night display will be automatically
151      * activated. One of {@link #AUTO_MODE_DISABLED}, {@link #AUTO_MODE_CUSTOM}, or
152      * {@link #AUTO_MODE_TWILIGHT}.
153      *
154      * @param autoMode the new auto mode to use
155      * @return {@code true} if new auto mode was set successfully
156      */
157     public boolean setAutoMode(@AutoMode int autoMode) {
158         if (autoMode != AUTO_MODE_DISABLED
159                 && autoMode != AUTO_MODE_CUSTOM
160                 && autoMode != AUTO_MODE_TWILIGHT) {
161             throw new IllegalArgumentException("Invalid autoMode: " + autoMode);
162         }
163
164         return Secure.putIntForUser(mContext.getContentResolver(),
165                 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
166     }
167
168     /**
169      * Returns the local time when Night display will be automatically activated when using
170      * {@link #AUTO_MODE_CUSTOM}.
171      */
172     public @NonNull LocalTime getCustomStartTime() {
173         int startTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
174                 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, -1, mUserId);
175         if (startTimeValue == -1) {
176             if (DEBUG) {
177                 Slog.d(TAG, "Using default value for setting: "
178                         + Secure.NIGHT_DISPLAY_CUSTOM_START_TIME);
179             }
180             startTimeValue = mContext.getResources().getInteger(
181                     R.integer.config_defaultNightDisplayCustomStartTime);
182         }
183
184         return LocalTime.valueOf(startTimeValue);
185     }
186
187     /**
188      * Sets the local time when Night display will be automatically activated when using
189      * {@link #AUTO_MODE_CUSTOM}.
190      *
191      * @param startTime the local time to automatically activate Night display
192      * @return {@code true} if the new custom start time was set successfully
193      */
194     public boolean setCustomStartTime(@NonNull LocalTime startTime) {
195         if (startTime == null) {
196             throw new IllegalArgumentException("startTime cannot be null");
197         }
198         return Secure.putIntForUser(mContext.getContentResolver(),
199                 Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
200     }
201
202     /**
203      * Returns the local time when Night display will be automatically deactivated when using
204      * {@link #AUTO_MODE_CUSTOM}.
205      */
206     public @NonNull LocalTime getCustomEndTime() {
207         int endTimeValue = Secure.getIntForUser(mContext.getContentResolver(),
208                 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, -1, mUserId);
209         if (endTimeValue == -1) {
210             if (DEBUG) {
211                 Slog.d(TAG, "Using default value for setting: "
212                         + Secure.NIGHT_DISPLAY_CUSTOM_END_TIME);
213             }
214             endTimeValue = mContext.getResources().getInteger(
215                     R.integer.config_defaultNightDisplayCustomEndTime);
216         }
217
218         return LocalTime.valueOf(endTimeValue);
219     }
220
221     /**
222      * Sets the local time when Night display will be automatically deactivated when using
223      * {@link #AUTO_MODE_CUSTOM}.
224      *
225      * @param endTime the local time to automatically deactivate Night display
226      * @return {@code true} if the new custom end time was set successfully
227      */
228     public boolean setCustomEndTime(@NonNull LocalTime endTime) {
229         if (endTime == null) {
230             throw new IllegalArgumentException("endTime cannot be null");
231         }
232         return Secure.putIntForUser(mContext.getContentResolver(),
233                 Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
234     }
235
236     private void onSettingChanged(@NonNull String setting) {
237         if (DEBUG) {
238             Slog.d(TAG, "onSettingChanged: " + setting);
239         }
240
241         if (mCallback != null) {
242             switch (setting) {
243                 case Secure.NIGHT_DISPLAY_ACTIVATED:
244                     mCallback.onActivated(isActivated());
245                     break;
246                 case Secure.NIGHT_DISPLAY_AUTO_MODE:
247                     mCallback.onAutoModeChanged(getAutoMode());
248                     break;
249                 case Secure.NIGHT_DISPLAY_CUSTOM_START_TIME:
250                     mCallback.onCustomStartTimeChanged(getCustomStartTime());
251                     break;
252                 case Secure.NIGHT_DISPLAY_CUSTOM_END_TIME:
253                     mCallback.onCustomEndTimeChanged(getCustomEndTime());
254                     break;
255             }
256         }
257     }
258
259     /**
260      * Register a callback to be invoked whenever the Night display settings are changed.
261      */
262     public void setListener(Callback callback) {
263         final Callback oldCallback = mCallback;
264         if (oldCallback != callback) {
265             mCallback = callback;
266
267             if (callback == null) {
268                 // Stop listening for changes now that there IS NOT a listener.
269                 mContext.getContentResolver().unregisterContentObserver(mContentObserver);
270             } else if (oldCallback == null) {
271                 // Start listening for changes now that there IS a listener.
272                 final ContentResolver cr = mContext.getContentResolver();
273                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_ACTIVATED),
274                         false /* notifyForDescendants */, mContentObserver, mUserId);
275                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_AUTO_MODE),
276                         false /* notifyForDescendants */, mContentObserver, mUserId);
277                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_START_TIME),
278                         false /* notifyForDescendants */, mContentObserver, mUserId);
279                 cr.registerContentObserver(Secure.getUriFor(Secure.NIGHT_DISPLAY_CUSTOM_END_TIME),
280                         false /* notifyForDescendants */, mContentObserver, mUserId);
281             }
282         }
283     }
284
285     /**
286      * Returns {@code true} if Night display is supported by the device.
287      */
288     public static boolean isAvailable(Context context) {
289         return context.getResources().getBoolean(R.bool.config_nightDisplayAvailable);
290     }
291
292     /**
293      * A time without a time-zone or date.
294      */
295     public static class LocalTime {
296
297         /**
298          * The hour of the day from 0 - 23.
299          */
300         public final int hourOfDay;
301         /**
302          * The minute within the hour from 0 - 59.
303          */
304         public final int minute;
305
306         public LocalTime(int hourOfDay, int minute) {
307             if (hourOfDay < 0 || hourOfDay > 23) {
308                 throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
309             } else if (minute < 0 || minute > 59) {
310                 throw new IllegalArgumentException("Invalid minute: " + minute);
311             }
312
313             this.hourOfDay = hourOfDay;
314             this.minute = minute;
315         }
316
317         /**
318          * Returns the first date time corresponding to this local time that occurs before the
319          * provided date time.
320          *
321          * @param time the date time to compare against
322          * @return the prior date time corresponding to this local time
323          */
324         public Calendar getDateTimeBefore(Calendar time) {
325             final Calendar c = Calendar.getInstance();
326             c.set(Calendar.YEAR, time.get(Calendar.YEAR));
327             c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
328
329             c.set(Calendar.HOUR_OF_DAY, hourOfDay);
330             c.set(Calendar.MINUTE, minute);
331             c.set(Calendar.SECOND, 0);
332             c.set(Calendar.MILLISECOND, 0);
333
334             // Check if the local time has past, if so return the same time tomorrow.
335             if (c.after(time)) {
336                 c.add(Calendar.DATE, -1);
337             }
338
339             return c;
340         }
341
342         /**
343          * Returns the first date time corresponding to this local time that occurs after the
344          * provided date time.
345          *
346          * @param time the date time to compare against
347          * @return the next date time corresponding to this local time
348          */
349         public Calendar getDateTimeAfter(Calendar time) {
350             final Calendar c = Calendar.getInstance();
351             c.set(Calendar.YEAR, time.get(Calendar.YEAR));
352             c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
353
354             c.set(Calendar.HOUR_OF_DAY, hourOfDay);
355             c.set(Calendar.MINUTE, minute);
356             c.set(Calendar.SECOND, 0);
357             c.set(Calendar.MILLISECOND, 0);
358
359             // Check if the local time has past, if so return the same time tomorrow.
360             if (c.before(time)) {
361                 c.add(Calendar.DATE, 1);
362             }
363
364             return c;
365         }
366
367         /**
368          * Returns a local time corresponding the given number of milliseconds from midnight.
369          *
370          * @param millis the number of milliseconds from midnight
371          * @return the corresponding local time
372          */
373         private static LocalTime valueOf(int millis) {
374             final int hourOfDay = (millis / 3600000) % 24;
375             final int minutes = (millis / 60000) % 60;
376             return new LocalTime(hourOfDay, minutes);
377         }
378
379         /**
380          * Returns the local time represented as milliseconds from midnight.
381          */
382         private int toMillis() {
383             return hourOfDay * 3600000 + minute * 60000;
384         }
385
386         @Override
387         public String toString() {
388             return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
389         }
390     }
391
392     /**
393      * Callback invoked whenever the Night display settings are changed.
394      */
395     public interface Callback {
396         /**
397          * Callback invoked when the activated state changes.
398          *
399          * @param activated {@code true} if Night display is activated
400          */
401         default void onActivated(boolean activated) {}
402         /**
403          * Callback invoked when the auto mode changes.
404          *
405          * @param autoMode the auto mode to use
406          */
407         default void onAutoModeChanged(int autoMode) {}
408         /**
409          * Callback invoked when the time to automatically activate Night display changes.
410          *
411          * @param startTime the local time to automatically activate Night display
412          */
413         default void onCustomStartTimeChanged(LocalTime startTime) {}
414         /**
415          * Callback invoked when the time to automatically deactivate Night display changes.
416          *
417          * @param endTime the local time to automatically deactivate Night display
418          */
419         default void onCustomEndTimeChanged(LocalTime endTime) {}
420     }
421 }