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

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

First pass at alarm snooze/reschedule; still pretty ugly.

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