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

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

Move discard alert text to resource. Fix listener for
EVENT_TIME_CHANGED I broke a few days ago. Only use
Alarm.mAbsoluteFireTime to measure real time if the alarm was
scheduled/rescheduled (through user action, not restoration from a
datastore) since boot. Don't display dates in the past as if they're
in the following week.

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