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