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
Line 
1package net.sabi.pester;
2
3import java.io.ByteArrayInputStream;
4import java.io.ByteArrayOutputStream;
5import java.io.DataInputStream;
6import java.io.DataOutputStream;
7import danger.app.Application;
8import danger.audio.RingToneObject;
9import danger.internal.Date;
10import danger.system.Hardware;
11import danger.text.Collator;
12import danger.util.LocaleUtils;
13import danger.util.StdActiveList;
14import danger.util.StdActiveObject;
15import danger.util.DEBUG;
16import danger.util.format.DateFormat;
17import danger.util.format.StringFormat;
18import java.util.Comparator;
19
20public class Alarm extends StdActiveObject implements Comparator {
21 private static final int VERSION_1 = 1;
22
23 // persisted
24 private String mMessage;
25 private int mType;
26 private int mPeriod;
27 private Date mDate;
28 private RingToneObject mAlert;
29
30 // transient
31 private int mState;
32 private int mAbsoluteFireTime; // only valid if periodic
33 private danger.app.Alarm mAlarm;
34 private int mUID;
35
36 public Alarm() {
37 mState = STATE_INVALID;
38 mAlarm = new danger.app.Alarm(0, Application.getCurrentApp(), this);
39 mUID = 0;
40 }
41
42 public String getMessage() {
43 return mMessage;
44 }
45 public int getPeriod() {
46 return mPeriod;
47 }
48 public boolean getUsesPeriod() {
49 return mType != TYPE_DATE;
50 }
51 public boolean getRepeating() {
52 return mType == TYPE_PERIODIC_REPEATING;
53 }
54 public Date getDate() {
55 return mDate;
56 }
57 public RingToneObject getAlert() {
58 return mAlert;
59 }
60 public int getUID() {
61 return mUID;
62 }
63
64 public void setMessage(String message) {
65 mMessage = message;
66 }
67 public void setPeriod(int period, boolean repeating) {
68 mType = repeating ? TYPE_PERIODIC_REPEATING : TYPE_PERIODIC;
69 mPeriod = period;
70 }
71 public void setDate(Date date) {
72 mType = TYPE_DATE;
73 mDate = date;
74 }
75 public void setAlert(RingToneObject alert) {
76 mAlert = alert.isValid() ? alert : null;
77 }
78 public void setUID(int uid) {
79 mUID = uid;
80 }
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);
89 dataStream.writeInt(mPeriod);
90 dataStream.writeInt(mDate.getUnixTimeGMT());
91 dataStream.writeInt(mType);
92 dataStream.writeInt(mAlert == null ? 0 : mAlert.getID());
93 dataStream.flush();
94 return byteStream.toByteArray();
95 } catch (Exception e) {
96 // XXX do something
97 DEBUG.p("failed to write alarm:" + e);
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();
112 mPeriod = dataStream.readInt();
113 mDate = new Date(dataStream.readInt());
114 mType = dataStream.readInt();
115 int alertID = dataStream.readInt();
116 mAlert = (alertID == 0 ? null : new RingToneObject(alertID));
117 } catch (Exception e) {
118 // XXX do something
119 DEBUG.p("failed to read alarm:" + e);
120 }
121 }
122
123 void beginEditing() {
124 mState = STATE_EDITING;
125 mAlarm.deactivate();
126 }
127
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
136 void resume() {
137 mState = STATE_SCHEDULED;
138 int interval = remainingInterval();
139 if (interval < 0) interval = 0;
140 mAlarm.resetWake(interval);
141 DEBUG.p("resetWake: " + description() + " - " + interval + "s left");
142 update();
143 }
144
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
158 void schedule() {
159 if (getUsesPeriod()) {
160 mDate = new Date();
161 mDate.addSeconds(mPeriod);
162 mAbsoluteFireTime = Hardware.getAbsoluteTime() + mPeriod;
163 }
164 resume();
165 }
166
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
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 }
186 switch (mType) {
187 case TYPE_PERIODIC:
188 sb.append("periodic (").append(mPeriod).append("s) "); break;
189 case TYPE_PERIODIC_REPEATING:
190 sb.append("repeating periodic (").append(mPeriod).append("s) ");
191 break;
192 case TYPE_DATE:
193 sb.append("date (").append(getDateTimeString()).append(") "); break;
194 }
195 sb.append("alarm: ");
196 sb.append('"').append(mMessage).append('"');
197 return sb.toString();
198 }
199
200 public static String dateTimeString(Date date, boolean relative) {
201 if (date == null)
202 return null;
203 String layout = LocaleUtils.getDateTimePattern();
204 String dateFormat = LocaleUtils.getMediumDateFormat();
205 String dateString;
206 String timeFormat;
207 if (relative) {
208 timeFormat = LocaleUtils.getShortTimeFormat();
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();
216 else
217 dateString = DateFormat.withFormat(dateFormat, date);
218 } else {
219 dateString = DateFormat.withFormat(dateFormat, date);
220 timeFormat = LocaleUtils.getMediumTimeFormat();
221 }
222 String timeString = DateFormat.withFormat(timeFormat, date);
223 return StringFormat.withFormat(layout, timeString, dateString);
224 }
225
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
259 public int getSecondsUntilNextIntervalStringUpdate() {
260 int i = remainingInterval();
261 if (i <= 1)
262 return 0;
263 if (i <= 60)
264 return 1;
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);
271 }
272
273 public String getDateTimeString(boolean relative) {
274 return dateTimeString(mDate, relative);
275 }
276
277 public String getDateTimeString() {
278 return getDateTimeString(false);
279 }
280
281 public String getIntervalString() {
282 return intervalString(remainingInterval());
283 }
284
285 public String toString() {
286 return getDateTimeString(true) + " - " + mMessage;
287 }
288
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
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
303 public static final int STATE_INVALID = 0;
304 public static final int STATE_EDITING = 1;
305 public static final int STATE_SCHEDULED = 2;
306}
Note: See TracBrowser for help on using the repository browser.