1/* 2 * Copyright (C) 2009 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.deskclock; 18 19import android.content.Context; 20import android.database.Cursor; 21import android.media.RingtoneManager; 22import android.net.Uri; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.provider.BaseColumns; 26 27import java.text.DateFormatSymbols; 28import java.util.Calendar; 29import java.util.HashMap; 30import java.util.HashSet; 31 32public final class Alarm implements Parcelable { 33 34 ////////////////////////////// 35 // Parcelable apis 36 ////////////////////////////// 37 public static final Parcelable.Creator<Alarm> CREATOR 38 = new Parcelable.Creator<Alarm>() { 39 public Alarm createFromParcel(Parcel p) { 40 return new Alarm(p); 41 } 42 43 public Alarm[] newArray(int size) { 44 return new Alarm[size]; 45 } 46 }; 47 48 public int describeContents() { 49 return 0; 50 } 51 52 public void writeToParcel(Parcel p, int flags) { 53 p.writeInt(id); 54 p.writeInt(enabled ? 1 : 0); 55 p.writeInt(hour); 56 p.writeInt(minutes); 57 p.writeInt(daysOfWeek.getCoded()); 58 p.writeLong(time); 59 p.writeInt(vibrate ? 1 : 0); 60 p.writeString(label); 61 p.writeParcelable(alert, flags); 62 p.writeInt(silent ? 1 : 0); 63 } 64 ////////////////////////////// 65 // end Parcelable apis 66 ////////////////////////////// 67 68 ////////////////////////////// 69 // Column definitions 70 ////////////////////////////// 71 public static class Columns implements BaseColumns { 72 /** 73 * The content:// style URL for this table 74 */ 75 public static final Uri CONTENT_URI = 76 Uri.parse("content://com.android.deskclock/alarm"); 77 78 /** 79 * Hour in 24-hour localtime 0 - 23. 80 * <P>Type: INTEGER</P> 81 */ 82 public static final String HOUR = "hour"; 83 84 /** 85 * Minutes in localtime 0 - 59 86 * <P>Type: INTEGER</P> 87 */ 88 public static final String MINUTES = "minutes"; 89 90 /** 91 * Days of week coded as integer 92 * <P>Type: INTEGER</P> 93 */ 94 public static final String DAYS_OF_WEEK = "daysofweek"; 95 96 /** 97 * Alarm time in UTC milliseconds from the epoch. 98 * <P>Type: INTEGER</P> 99 */ 100 public static final String ALARM_TIME = "alarmtime"; 101 102 /** 103 * True if alarm is active 104 * <P>Type: BOOLEAN</P> 105 */ 106 public static final String ENABLED = "enabled"; 107 108 /** 109 * True if alarm should vibrate 110 * <P>Type: BOOLEAN</P> 111 */ 112 public static final String VIBRATE = "vibrate"; 113 114 /** 115 * Message to show when alarm triggers 116 * Note: not currently used 117 * <P>Type: STRING</P> 118 */ 119 public static final String MESSAGE = "message"; 120 121 /** 122 * Audio alert to play when alarm triggers 123 * <P>Type: STRING</P> 124 */ 125 public static final String ALERT = "alert"; 126 127 /** 128 * The default sort order for this table 129 */ 130 public static final String DEFAULT_SORT_ORDER = 131 HOUR + ", " + MINUTES + " ASC" + ", " + _ID + " DESC"; 132 133 // Used when filtering enabled alarms. 134 public static final String WHERE_ENABLED = ENABLED + "=1"; 135 136 static final String[] ALARM_QUERY_COLUMNS = { 137 _ID, HOUR, MINUTES, DAYS_OF_WEEK, ALARM_TIME, 138 ENABLED, VIBRATE, MESSAGE, ALERT }; 139 140 /** 141 * These save calls to cursor.getColumnIndexOrThrow() 142 * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS 143 */ 144 public static final int ALARM_ID_INDEX = 0; 145 public static final int ALARM_HOUR_INDEX = 1; 146 public static final int ALARM_MINUTES_INDEX = 2; 147 public static final int ALARM_DAYS_OF_WEEK_INDEX = 3; 148 public static final int ALARM_TIME_INDEX = 4; 149 public static final int ALARM_ENABLED_INDEX = 5; 150 public static final int ALARM_VIBRATE_INDEX = 6; 151 public static final int ALARM_MESSAGE_INDEX = 7; 152 public static final int ALARM_ALERT_INDEX = 8; 153 } 154 ////////////////////////////// 155 // End column definitions 156 ////////////////////////////// 157 158 // Public fields 159 public int id; 160 public boolean enabled; 161 public int hour; 162 public int minutes; 163 public DaysOfWeek daysOfWeek; 164 public long time; 165 public boolean vibrate; 166 public String label; 167 public Uri alert; 168 public boolean silent; 169 170 @Override 171 public String toString() { 172 return "Alarm{" + 173 "alert=" + alert + 174 ", id=" + id + 175 ", enabled=" + enabled + 176 ", hour=" + hour + 177 ", minutes=" + minutes + 178 ", daysOfWeek=" + daysOfWeek + 179 ", time=" + time + 180 ", vibrate=" + vibrate + 181 ", label='" + label + '\'' + 182 ", silent=" + silent + 183 '}'; 184 } 185 186 public Alarm(Cursor c) { 187 id = c.getInt(Columns.ALARM_ID_INDEX); 188 enabled = c.getInt(Columns.ALARM_ENABLED_INDEX) == 1; 189 hour = c.getInt(Columns.ALARM_HOUR_INDEX); 190 minutes = c.getInt(Columns.ALARM_MINUTES_INDEX); 191 daysOfWeek = new DaysOfWeek(c.getInt(Columns.ALARM_DAYS_OF_WEEK_INDEX)); 192 time = c.getLong(Columns.ALARM_TIME_INDEX); 193 vibrate = c.getInt(Columns.ALARM_VIBRATE_INDEX) == 1; 194 label = c.getString(Columns.ALARM_MESSAGE_INDEX); 195 String alertString = c.getString(Columns.ALARM_ALERT_INDEX); 196 if (Alarms.ALARM_ALERT_SILENT.equals(alertString)) { 197 if (Log.LOGV) { 198 Log.v("Alarm is marked as silent"); 199 } 200 silent = true; 201 } else { 202 if (alertString != null && alertString.length() != 0) { 203 alert = Uri.parse(alertString); 204 } 205 206 // If the database alert is null or it failed to parse, use the 207 // default alert. 208 if (alert == null) { 209 alert = RingtoneManager.getDefaultUri( 210 RingtoneManager.TYPE_ALARM); 211 } 212 } 213 } 214 215 public Alarm(Parcel p) { 216 id = p.readInt(); 217 enabled = p.readInt() == 1; 218 hour = p.readInt(); 219 minutes = p.readInt(); 220 daysOfWeek = new DaysOfWeek(p.readInt()); 221 time = p.readLong(); 222 vibrate = p.readInt() == 1; 223 label = p.readString(); 224 alert = (Uri) p.readParcelable(null); 225 silent = p.readInt() == 1; 226 } 227 228 // Creates a default alarm at the current time. 229 public Alarm() { 230 id = -1; 231 hour = 0; 232 minutes = 0; 233 vibrate = true; 234 daysOfWeek = new DaysOfWeek(0); 235 label = ""; 236 alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); 237 } 238 239 public String getLabelOrDefault(Context context) { 240 if (label == null || label.length() == 0) { 241 return context.getString(R.string.default_label); 242 } 243 return label; 244 } 245 246 @Override 247 public int hashCode() { 248 return id; 249 } 250 251 @Override 252 public boolean equals(Object o) { 253 if (!(o instanceof Alarm)) return false; 254 final Alarm other = (Alarm) o; 255 return id == other.id; 256 } 257 258 259 /* 260 * Days of week code as a single int. 261 * 0x00: no day 262 * 0x01: Monday 263 * 0x02: Tuesday 264 * 0x04: Wednesday 265 * 0x08: Thursday 266 * 0x10: Friday 267 * 0x20: Saturday 268 * 0x40: Sunday 269 */ 270 static final class DaysOfWeek { 271 272 private static int[] DAY_MAP = new int[] { 273 Calendar.MONDAY, 274 Calendar.TUESDAY, 275 Calendar.WEDNESDAY, 276 Calendar.THURSDAY, 277 Calendar.FRIDAY, 278 Calendar.SATURDAY, 279 Calendar.SUNDAY, 280 }; 281 282 283 private static HashMap<Integer, Integer> DAY_TO_BIT_MASK = new HashMap<Integer, Integer>(); 284 static { 285 for (int i = 0; i < DAY_MAP.length; i++) { 286 DAY_TO_BIT_MASK.put(DAY_MAP[i], i); 287 } 288 } 289 290 // Bitmask of all repeating days 291 private int mDays; 292 293 DaysOfWeek(int days) { 294 mDays = days; 295 } 296 297 public String toString(Context context, boolean showNever) { 298 return toString(context, showNever, false); 299 } 300 301 public String toAccessibilityString(Context context) { 302 return toString(context, false, true); 303 } 304 305 private String toString(Context context, boolean showNever, boolean forAccessibility) { 306 StringBuilder ret = new StringBuilder(); 307 308 // no days 309 if (mDays == 0) { 310 return showNever ? 311 context.getText(R.string.never).toString() : ""; 312 } 313 314 // every day 315 if (mDays == 0x7f) { 316 return context.getText(R.string.every_day).toString(); 317 } 318 319 // count selected days 320 int dayCount = 0, days = mDays; 321 while (days > 0) { 322 if ((days & 1) == 1) dayCount++; 323 days >>= 1; 324 } 325 326 // short or long form? 327 DateFormatSymbols dfs = new DateFormatSymbols(); 328 String[] dayList = (forAccessibility || dayCount <= 1) ? 329 dfs.getWeekdays() : 330 dfs.getShortWeekdays(); 331 332 // selected days 333 for (int i = 0; i < 7; i++) { 334 if ((mDays & (1 << i)) != 0) { 335 ret.append(dayList[DAY_MAP[i]]); 336 dayCount -= 1; 337 if (dayCount > 0) ret.append( 338 context.getText(R.string.day_concat)); 339 } 340 } 341 return ret.toString(); 342 } 343 344 private boolean isSet(int day) { 345 return ((mDays & (1 << day)) > 0); 346 } 347 348 /** 349 * Sets the repeat day for the alarm. 350 * 351 * @param dayOfWeek One of: Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, etc. 352 * @param set Whether to set or unset. 353 */ 354 public void setDayOfWeek(int dayOfWeek, boolean set) { 355 final int bitIndex = DAY_TO_BIT_MASK.get(dayOfWeek); 356 set(bitIndex, set); 357 } 358 359 public void set(int day, boolean set) { 360 if (set) { 361 mDays |= (1 << day); 362 } else { 363 mDays &= ~(1 << day); 364 } 365 } 366 367 public void set(DaysOfWeek dow) { 368 mDays = dow.mDays; 369 } 370 371 public int getCoded() { 372 return mDays; 373 } 374 375 public HashSet<Integer> getSetDays() { 376 final HashSet<Integer> set = new HashSet<Integer>(); 377 for (int i = 0; i < 7; i++) { 378 if (isSet(i)) { 379 set.add(DAY_MAP[i]); 380 } 381 } 382 return set; 383 } 384 385 // Returns days of week encoded in an array of booleans. 386 public boolean[] getBooleanArray() { 387 boolean[] ret = new boolean[7]; 388 for (int i = 0; i < 7; i++) { 389 ret[i] = isSet(i); 390 } 391 return ret; 392 } 393 394 public boolean isRepeatSet() { 395 return mDays != 0; 396 } 397 398 /** 399 * returns number of days from today until next alarm 400 * @param c must be set to today 401 */ 402 public int getNextAlarm(Calendar c) { 403 if (mDays == 0) { 404 return -1; 405 } 406 407 int today = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7; 408 409 int day = 0; 410 int dayCount = 0; 411 for (; dayCount < 7; dayCount++) { 412 day = (today + dayCount) % 7; 413 if (isSet(day)) { 414 break; 415 } 416 } 417 return dayCount; 418 } 419 420 @Override 421 public String toString() { 422 return "DaysOfWeek{" + 423 "mDays=" + mDays + 424 '}'; 425 } 426 } 427} 428