Home | History | Annotate | Download | only in deskclock
      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 import java.util.HashMap;
     30 import java.util.HashSet;
     31 
     32 public 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