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

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

Tooltip updating (which flashes on the device. I'm gonna kill someone...)

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