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