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

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

Handle time changes by rescheduling (if it's time-based) or just updating the fire time (if it's interval-based, since those use real time when active).

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