1 /* 2 * Copyright (C) 2013 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.deskclock.provider; 18 19 import android.content.Context; 20 21 import com.android.deskclock.R; 22 23 import java.text.DateFormatSymbols; 24 import java.util.Calendar; 25 import java.util.HashSet; 26 27 /* 28 * Days of week code as a single int. 29 * 0x00: no day 30 * 0x01: Monday 31 * 0x02: Tuesday 32 * 0x04: Wednesday 33 * 0x08: Thursday 34 * 0x10: Friday 35 * 0x20: Saturday 36 * 0x40: Sunday 37 */ 38 public final class DaysOfWeek { 39 // Number if days in the week. 40 public static final int DAYS_IN_A_WEEK = 7; 41 42 // Value when all days are set 43 public static final int ALL_DAYS_SET = 0x7f; 44 45 // Value when no days are set 46 public static final int NO_DAYS_SET = 0; 47 48 /** 49 * Need to have monday start at index 0 to be backwards compatible. This converts 50 * Calendar.DAY_OF_WEEK constants to our internal bit structure. 51 */ 52 static int convertDayToBitIndex(int day) { 53 return (day + 5) % DAYS_IN_A_WEEK; 54 } 55 56 /** 57 * Need to have monday start at index 0 to be backwards compatible. This converts 58 * our bit structure to Calendar.DAY_OF_WEEK constant value. 59 */ 60 static int convertBitIndexToDay(int bitIndex) { 61 return (bitIndex + 1) % DAYS_IN_A_WEEK + 1; 62 } 63 64 // Bitmask of all repeating days 65 private int mBitSet; 66 67 public DaysOfWeek(int bitSet) { 68 mBitSet = bitSet; 69 } 70 71 public String toString(Context context, int firstDay) { 72 return toString(context, firstDay, false /* forAccessibility */); 73 } 74 75 public String toAccessibilityString(Context context, int firstDay) { 76 return toString(context, firstDay, true /* forAccessibility */); 77 } 78 79 private String toString(Context context, int firstDay, boolean forAccessibility) { 80 StringBuilder ret = new StringBuilder(); 81 82 // no days 83 if (mBitSet == NO_DAYS_SET) { 84 return ""; 85 } 86 87 // every day 88 if (mBitSet == ALL_DAYS_SET) { 89 return context.getText(R.string.every_day).toString(); 90 } 91 92 // count selected days 93 int dayCount = 0; 94 int bitSet = mBitSet; 95 while (bitSet > 0) { 96 if ((bitSet & 1) == 1) dayCount++; 97 bitSet >>= 1; 98 } 99 100 // short or long form? 101 DateFormatSymbols dfs = new DateFormatSymbols(); 102 String[] dayList = (forAccessibility || dayCount <= 1) ? 103 dfs.getWeekdays() : 104 dfs.getShortWeekdays(); 105 106 // In this system, Mon = 0, Sun = 6, etc. 107 // startDay is stored corresponding to Calendar.DAY_OF_WEEK where Sun = 0, Mon = 2, etc. 108 final int startDay = convertDayToBitIndex(firstDay); 109 110 // selected days, starting from user-selected start day of week 111 // iterate starting from user-selected start of day 112 for (int bitIndex = startDay; bitIndex < DAYS_IN_A_WEEK + startDay; ++bitIndex) { 113 if ((mBitSet & (1 << (bitIndex % DAYS_IN_A_WEEK))) != 0) { 114 ret.append(dayList[convertBitIndexToDay(bitIndex)]); 115 dayCount -= 1; 116 if (dayCount > 0) ret.append(context.getText(R.string.day_concat)); 117 } 118 } 119 return ret.toString(); 120 } 121 122 /** 123 * Enables or disable certain days of the week. 124 * 125 * @param daysOfWeek Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, etc. 126 */ 127 public void setDaysOfWeek(boolean value, int ... daysOfWeek) { 128 for (int day : daysOfWeek) { 129 setBit(convertDayToBitIndex(day), value); 130 } 131 } 132 133 private boolean isBitEnabled(int bitIndex) { 134 return ((mBitSet & (1 << bitIndex)) > 0); 135 } 136 137 private void setBit(int bitIndex, boolean set) { 138 if (set) { 139 mBitSet |= (1 << bitIndex); 140 } else { 141 mBitSet &= ~(1 << bitIndex); 142 } 143 } 144 145 public void setBitSet(int bitSet) { 146 mBitSet = bitSet; 147 } 148 149 public int getBitSet() { 150 return mBitSet; 151 } 152 153 /** 154 * Returns set of Calendar.MONDAY, Calendar.TUESDAY, etc based on the current mBitSet value 155 */ 156 public HashSet<Integer> getSetDays() { 157 final HashSet<Integer> result = new HashSet<Integer>(); 158 for (int bitIndex = 0; bitIndex < DAYS_IN_A_WEEK; bitIndex++) { 159 if (isBitEnabled(bitIndex)) { 160 result.add(convertBitIndexToDay(bitIndex)); 161 } 162 } 163 return result; 164 } 165 166 public boolean isRepeating() { 167 return mBitSet != NO_DAYS_SET; 168 } 169 170 /** 171 * Returns number of days backwards from today to previous alarm. 172 * ex: 173 * Daily alarm, current = Tuesday -> 1 174 * Weekly alarm on Wednesday, current = Tuesday -> 6 175 * One time alarm -> -1 176 * 177 * @param current must be set to today 178 */ 179 public int calculateDaysToPreviousAlarm(Calendar current) { 180 if (!isRepeating()) { 181 return -1; 182 } 183 184 // We only use this on preemptively dismissed alarms, and alarms can only fire once a day, 185 // so there is no chance that the previous fire time is on the same day. Start dayCount on 186 // previous day. 187 int dayCount = -1; 188 int currentDayIndex = convertDayToBitIndex(current.get(Calendar.DAY_OF_WEEK)); 189 for (; dayCount >= -DAYS_IN_A_WEEK; dayCount--) { 190 int previousAlarmBitIndex = (currentDayIndex + dayCount); 191 if (previousAlarmBitIndex < 0) { 192 // Ex. previousAlarmBitIndex = -1 means the day before index 0 = index 6 193 previousAlarmBitIndex = previousAlarmBitIndex + DAYS_IN_A_WEEK; 194 } 195 if (isBitEnabled(previousAlarmBitIndex)) { 196 break; 197 } 198 } 199 // return a positive value 200 return dayCount * -1; 201 } 202 203 /** 204 * Returns number of days from today until next alarm. 205 * 206 * @param current must be set to today or the day after the currentTime 207 */ 208 public int calculateDaysToNextAlarm(Calendar current) { 209 if (!isRepeating()) { 210 return -1; 211 } 212 213 int dayCount = 0; 214 int currentDayIndex = convertDayToBitIndex(current.get(Calendar.DAY_OF_WEEK)); 215 for (; dayCount < DAYS_IN_A_WEEK; dayCount++) { 216 int nextAlarmBitIndex = (currentDayIndex + dayCount) % DAYS_IN_A_WEEK; 217 if (isBitEnabled(nextAlarmBitIndex)) { 218 break; 219 } 220 } 221 return dayCount; 222 } 223 224 public void clearAllDays() { 225 mBitSet = NO_DAYS_SET; 226 } 227 228 @Override 229 public String toString() { 230 return "DaysOfWeek{" + 231 "mBitSet=" + mBitSet + 232 '}'; 233 } 234 } 235