source: trunk/hiptop/pester/net/sabi/pester/Alarm.java@ 276

Last change on this file since 276 was 276, checked in by Nicholas Riley, 18 years ago

First pass at alarm persistence.

File size: 8.1 KB
RevLine 
[237]1package net.sabi.pester;
2
3import java.io.ByteArrayInputStream;
4import java.io.ByteArrayOutputStream;
5import java.io.DataInputStream;
6import java.io.DataOutputStream;
[242]7import danger.app.Application;
[244]8import danger.audio.RingToneObject;
[237]9import danger.internal.Date;
[255]10import danger.system.Hardware;
[259]11import danger.text.Collator;
[243]12import danger.util.LocaleUtils;
[242]13import danger.util.StdActiveList;
[237]14import danger.util.StdActiveObject;
[240]15import danger.util.DEBUG;
[243]16import danger.util.format.DateFormat;
17import danger.util.format.StringFormat;
[259]18import java.util.Comparator;
[237]19
[259]20public class Alarm extends StdActiveObject implements Comparator {
[237]21 private static final int VERSION_1 = 1;
22
23 // persisted
24 private String mMessage;
[239]25 private int mType;
[250]26 private int mPeriod;
[237]27 private Date mDate;
[244]28 private RingToneObject mAlert;
[237]29
30 // transient
[241]31 private int mState;
[255]32 private int mAbsoluteFireTime; // only valid if periodic
[242]33 private danger.app.Alarm mAlarm;
[276]34 private int mUID;
[237]35
36 public Alarm() {
[241]37 mState = STATE_INVALID;
[242]38 mAlarm = new danger.app.Alarm(0, Application.getCurrentApp(), this);
[276]39 mUID = 0;
[237]40 }
41
42 public String getMessage() {
43 return mMessage;
44 }
[240]45 public int getPeriod() {
[250]46 return mPeriod;
[237]47 }
48 public boolean getUsesPeriod() {
[239]49 return mType != TYPE_DATE;
[237]50 }
[240]51 public boolean getRepeating() {
52 return mType == TYPE_PERIODIC_REPEATING;
53 }
[237]54 public Date getDate() {
55 return mDate;
56 }
[244]57 public RingToneObject getAlert() {
58 return mAlert;
59 }
[276]60 public int getUID() {
61 return mUID;
62 }
[237]63
64 public void setMessage(String message) {
65 mMessage = message;
66 }
[240]67 public void setPeriod(int period, boolean repeating) {
[239]68 mType = repeating ? TYPE_PERIODIC_REPEATING : TYPE_PERIODIC;
[250]69 mPeriod = period;
[237]70 }
71 public void setDate(Date date) {
[239]72 mType = TYPE_DATE;
[237]73 mDate = date;
74 }
[244]75 public void setAlert(RingToneObject alert) {
76 mAlert = alert.isValid() ? alert : null;
77 }
[276]78 public void setUID(int uid) {
79 mUID = uid;
80 }
[237]81
82 public byte[] toByteArray() {
83 try {
84 ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
85 DataOutputStream dataStream = new DataOutputStream(byteStream);
86
87 dataStream.writeByte(VERSION_1);
88 dataStream.writeUTF(mMessage);
[250]89 dataStream.writeInt(mPeriod);
[237]90 dataStream.writeInt(mDate.getUnixTimeGMT());
[239]91 dataStream.writeInt(mType);
[253]92 dataStream.writeInt(mAlert == null ? 0 : mAlert.getID());
[237]93 dataStream.flush();
94 return byteStream.toByteArray();
95 } catch (Exception e) {
96 // XXX do something
[253]97 DEBUG.p("failed to write alarm:" + e);
[237]98 }
99 return null;
100 }
101
102 public void fromByteArray(byte[] data) {
103 try {
104 ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
105 DataInputStream dataStream = new DataInputStream(byteStream);
106
107 byte version = dataStream.readByte();
108 if (version != VERSION_1) {
109 // XXX barf
110 }
111 mMessage = dataStream.readUTF();
[250]112 mPeriod = dataStream.readInt();
[253]113 mDate = new Date(dataStream.readInt());
[239]114 mType = dataStream.readInt();
[253]115 int alertID = dataStream.readInt();
116 mAlert = (alertID == 0 ? null : new RingToneObject(alertID));
[237]117 } catch (Exception e) {
118 // XXX do something
[253]119 DEBUG.p("failed to read alarm:" + e);
[237]120 }
121 }
122
123 void beginEditing() {
[241]124 mState = STATE_EDITING;
[242]125 mAlarm.deactivate();
[237]126 }
127
[249]128 public static int secondsFromNow(Date date) {
129 return date.getDangerTimeGMT() - new Date().getDangerTimeGMT();
130 }
131
132 protected int remainingInterval() {
133 return secondsFromNow(mDate);
134 }
135
[243]136 void resume() {
137 mState = STATE_SCHEDULED;
[249]138 int interval = remainingInterval();
139 if (interval < 0) interval = 0;
140 mAlarm.resetWake(interval);
141 DEBUG.p("resetWake: " + description() + " - " + interval + "s left");
[243]142 update();
143 }
144
[255]145 void timeChanged() {
146 if (mState != STATE_SCHEDULED)
147 return;
148 if (getUsesPeriod()) {
149 int realSecondsLeft = mAbsoluteFireTime - Hardware.getAbsoluteTime();
150 mDate = new Date();
151 mDate.addSeconds(realSecondsLeft < 0 ? 0 : realSecondsLeft);
152 // no need to call resume(): alarm uses absolute time
153 } else {
154 resume();
155 }
156 }
157
[243]158 void schedule() {
159 if (getUsesPeriod()) {
160 mDate = new Date();
[250]161 mDate.addSeconds(mPeriod);
[255]162 mAbsoluteFireTime = Hardware.getAbsoluteTime() + mPeriod;
[243]163 }
164 resume();
165 }
166
[242]167 void dismiss() {
168 if (getRepeating())
169 schedule();
170 else
171 ((StdActiveList)getDelegate()).removeItem(this);
172 }
173
174 void cancel() {
175 mState = STATE_INVALID;
176 mAlarm.deactivate();
177 }
178
[243]179 public String description() {
180 StringBuffer sb = new StringBuffer();
181 switch (mState) {
182 case STATE_INVALID: sb.append("invalid "); break;
183 case STATE_EDITING: sb.append("editing "); break;
184 case STATE_SCHEDULED: sb.append("scheduled "); break;
185 }
[240]186 switch (mType) {
187 case TYPE_PERIODIC:
[250]188 sb.append("periodic (").append(mPeriod).append("s) "); break;
[240]189 case TYPE_PERIODIC_REPEATING:
[250]190 sb.append("repeating periodic (").append(mPeriod).append("s) ");
[243]191 break;
[240]192 case TYPE_DATE:
[243]193 sb.append("date (").append(getDateTimeString()).append(") "); break;
[240]194 }
[243]195 sb.append("alarm: ");
196 sb.append('"').append(mMessage).append('"');
197 return sb.toString();
[237]198 }
[243]199
[249]200 public static String dateTimeString(Date date, boolean relative) {
[243]201 if (date == null)
202 return null;
203 String layout = LocaleUtils.getDateTimePattern();
[247]204 String dateFormat = LocaleUtils.getMediumDateFormat();
[250]205 String dateString;
206 String timeFormat;
[249]207 if (relative) {
[250]208 timeFormat = LocaleUtils.getShortTimeFormat();
[249]209 int daysFromToday = date.getDaysBetween(new Date());
210 if (daysFromToday == 0)
211 dateString = "Today"; // XXX localize
212 else if (daysFromToday == 1)
213 dateString = "Tomorrow"; // XXX localize
214 else if (daysFromToday < 7)
215 dateString = date.getDayString();
[250]216 else
217 dateString = DateFormat.withFormat(dateFormat, date);
218 } else {
219 dateString = DateFormat.withFormat(dateFormat, date);
220 timeFormat = LocaleUtils.getMediumTimeFormat();
[249]221 }
[250]222 String timeString = DateFormat.withFormat(timeFormat, date);
[249]223 return StringFormat.withFormat(layout, timeString, dateString);
[243]224 }
225
[249]226 protected static void addUnit(StringBuffer sb, int n, String unit) {
227 if (n == 0) return;
228 if (sb.length() > 0) sb.append(", ");
229 if (n == 1) sb.append("one ").append(unit);
230 else sb.append(n).append(' ').append(unit).append("s");
231 }
232
233 public static String intervalString(int i) { // XXX localize
234 if (i < 0)
235 return "expired";
236 StringBuffer sb = new StringBuffer();
237 if (i < 60) {
238 addUnit(sb, i, "second");
239 } else {
240 i /= 60; // minutes
241 if (i < 24 * 60) {
242 addUnit(sb, i / 60, "hour");
243 addUnit(sb, i % 60, "minute");
244 } else {
245 i /= 60; // hours
246 if (i < 365) {
247 addUnit(sb, i / 24, "day");
248 addUnit(sb, i % 24, "hour");
249 } else {
250 i /= 24; // days
251 addUnit(sb, i / 365, "year"); // XXX actually 365.242199
252 addUnit(sb, i % 365, "day");
253 }
254 }
255 }
256 return sb.toString();
257 }
258
[273]259 public int getSecondsUntilNextIntervalStringUpdate() {
260 int i = remainingInterval();
[275]261 if (i <= 1)
[273]262 return 0;
263 if (i <= 60)
264 return 1;
[275]265 if (i <= 24 * 60 * 60) {
266 i %= 60;
267 return (i == 0 ? 60 : i);
268 }
269 i %= 3600;
270 return (i == 0 ? 3600 : i);
[273]271 }
272
[249]273 public String getDateTimeString(boolean relative) {
274 return dateTimeString(mDate, relative);
275 }
276
[243]277 public String getDateTimeString() {
[249]278 return getDateTimeString(false);
[243]279 }
[249]280
281 public String getIntervalString() {
282 return intervalString(remainingInterval());
283 }
[243]284
[237]285 public String toString() {
[249]286 return getDateTimeString(true) + " - " + mMessage;
[237]287 }
288
[259]289 public int compare(Object arg0, Object arg1) {
290 Alarm alarm0 = (Alarm)arg0, alarm1 = (Alarm)arg1;
291 int result = alarm0.getDate().compareTo(alarm1.getDate());
292 if (result != 0) return result;
293 result = Collator.getInstance().compare(alarm0.getMessage(),
294 alarm1.getMessage());
295 if (result != 0) return result;
296 return alarm0.hashCode() - alarm1.hashCode();
297 }
298
[239]299 public static final int TYPE_PERIODIC = 0;
300 public static final int TYPE_PERIODIC_REPEATING = 1;
301 public static final int TYPE_DATE = 2;
302
[241]303 public static final int STATE_INVALID = 0;
304 public static final int STATE_EDITING = 1;
305 public static final int STATE_SCHEDULED = 2;
[237]306}
Note: See TracBrowser for help on using the repository browser.