1/**
2 * Copyright (c) 2014, 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
17package com.android.server.notification;
18
19import java.util.Calendar;
20import java.util.Objects;
21import java.util.TimeZone;
22
23import android.service.notification.ZenModeConfig;
24import android.service.notification.ZenModeConfig.DowntimeInfo;
25import android.util.ArraySet;
26
27public class DowntimeCalendar {
28
29    private final ArraySet<Integer> mDays = new ArraySet<Integer>();
30    private final Calendar mCalendar = Calendar.getInstance();
31
32    private DowntimeInfo mInfo;
33
34    @Override
35    public String toString() {
36        return "DowntimeCalendar[mDays=" + mDays + "]";
37    }
38
39    public void setDowntimeInfo(DowntimeInfo info) {
40        if (Objects.equals(mInfo, info)) return;
41        mInfo = info;
42        updateDays();
43    }
44
45    public long nextDowntimeStart(long time) {
46        if (mInfo == null || mDays.size() == 0) return Long.MAX_VALUE;
47        final long start = getTime(time, mInfo.startHour, mInfo.startMinute);
48        for (int i = 0; i < Calendar.SATURDAY; i++) {
49            final long t = addDays(start, i);
50            if (t > time && isInDowntime(t)) {
51                return t;
52            }
53        }
54        return Long.MAX_VALUE;
55    }
56
57    public void setTimeZone(TimeZone tz) {
58        mCalendar.setTimeZone(tz);
59    }
60
61    public long getNextTime(long now, int hr, int min) {
62        final long time = getTime(now, hr, min);
63        return time <= now ? addDays(time, 1) : time;
64    }
65
66    private long getTime(long millis, int hour, int min) {
67        mCalendar.setTimeInMillis(millis);
68        mCalendar.set(Calendar.HOUR_OF_DAY, hour);
69        mCalendar.set(Calendar.MINUTE, min);
70        mCalendar.set(Calendar.SECOND, 0);
71        mCalendar.set(Calendar.MILLISECOND, 0);
72        return mCalendar.getTimeInMillis();
73    }
74
75    public boolean isInDowntime(long time) {
76        if (mInfo == null || mDays.size() == 0) return false;
77        final long start = getTime(time, mInfo.startHour, mInfo.startMinute);
78        long end = getTime(time, mInfo.endHour, mInfo.endMinute);
79        if (end <= start) {
80            end = addDays(end, 1);
81        }
82        return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
83    }
84
85    private boolean isInDowntime(int daysOffset, long time, long start, long end) {
86        final int n = Calendar.SATURDAY;
87        final int day = ((getDayOfWeek(time) - 1) + (daysOffset % n) + n) % n + 1;
88        start = addDays(start, daysOffset);
89        end = addDays(end, daysOffset);
90        return mDays.contains(day) && time >= start && time < end;
91    }
92
93    private int getDayOfWeek(long time) {
94        mCalendar.setTimeInMillis(time);
95        return mCalendar.get(Calendar.DAY_OF_WEEK);
96    }
97
98    private void updateDays() {
99        mDays.clear();
100        if (mInfo != null) {
101            final int[] days = ZenModeConfig.tryParseDays(mInfo.mode);
102            for (int i = 0; days != null && i < days.length; i++) {
103                mDays.add(days[i]);
104            }
105        }
106    }
107
108    private long addDays(long time, int days) {
109        mCalendar.setTimeInMillis(time);
110        mCalendar.add(Calendar.DATE, days);
111        return mCalendar.getTimeInMillis();
112    }
113}
114