source: trunk/hiptop/pester/net/sabi/pester/AlarmSetDialog.java@ 288

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

Nicer rescheduling support; cancel reschedule should only be supported for repeating alarms (for non-repeating ones, you can only discard).

File size: 9.5 KB
Line 
1package net.sabi.pester;
2
3import danger.app.Application;
4import danger.app.Event;
5import danger.audio.ToneClass;
6import danger.audio.ToneRights;
7import danger.internal.Date;
8import danger.ui.Button;
9import danger.ui.CheckBox;
10import danger.ui.Color;
11import danger.ui.DateTimeEditor;
12import danger.ui.DatePicker;
13import danger.ui.DialogWindow;
14import danger.ui.Font;
15import danger.ui.Layout;
16import danger.ui.MenuItem;
17import danger.ui.NumberEditor;
18import danger.ui.Pen;
19import danger.ui.PopupMenu;
20import danger.ui.RadioButton;
21import danger.ui.Rect;
22import danger.ui.RingTonePicker;
23import danger.ui.StaticTextBox;
24import danger.ui.TypeAheadTextField;
25import danger.ui.View;
26import danger.util.DEBUG;
27
28public class AlarmSetDialog extends DialogWindow
29 implements Resources, Commands {
30
31 private TypeAheadTextField mMessageField;
32 private RadioButton mInButton, mAtButton;
33 private NumberEditor mPeriodField;
34 private PopupMenu mPeriodUnitsPopup;
35 // XXX don't allow <5 second repeating alarms
36 private CheckBox mRepeatCheckBox;
37 private DateTimeEditor mTimeEditor, mDateEditor;
38 private DatePicker mDatePicker;
39 private RingTonePicker mAlertPicker;
40 private Button mDiscardAlarmButton, mSetAlarmButton;
41 private StaticTextBox mStatusBox;
42
43 private Alarm mEditingAlarm;
44 private boolean mAlarmIsNew;
45 private boolean mHaveShownWindow = false;
46
47 private danger.app.Alarm mValidateAlarm;
48
49 public static AlarmSetDialog getDialog() {
50 AlarmSetDialog dialog = (AlarmSetDialog)
51 Application.getCurrentApp().getDialog(ID_ALARM_SET_DIALOG, null);
52 return dialog;
53 }
54
55 public void onDecoded() {
56 setCancelButtonEvent(new Event(this, EVENT_CANCEL_ALARM));
57 mMessageField =
58 (TypeAheadTextField)getDescendantWithID(ID_MESSAGE_FIELD);
59 mInButton = (RadioButton)getDescendantWithID(ID_IN_BUTTON);
60 mAtButton = (RadioButton)getDescendantWithID(ID_AT_BUTTON);
61 mPeriodField = (NumberEditor)getDescendantWithID(ID_PERIOD_FIELD);
62 mPeriodUnitsPopup =
63 (PopupMenu)getDescendantWithID(ID_PERIOD_UNITS_POPUP);
64 mRepeatCheckBox =
65 (CheckBox)getDescendantWithID(ID_PERIOD_REPEAT_CHECKBOX);
66 mTimeEditor = (DateTimeEditor)getDescendantWithID(ID_TIME_EDITOR);
67 mDateEditor = (DateTimeEditor)getDescendantWithID(ID_DATE_EDITOR);
68 mDatePicker = (DatePicker)getDescendantWithID(ID_DATE_PICKER);
69 mAlertPicker = (RingTonePicker)getDescendantWithID(ID_ALERT_PICKER);
70 mSetAlarmButton = (Button)getDescendantWithID(ID_SET_ALARM_BUTTON);
71 mDiscardAlarmButton =
72 (Button)getDescendantWithID(ID_DISCARD_ALARM_BUTTON);
73 mMessageField.setTextFinder(new MessageFinder());
74 mMessageField.setNumCharsNeededForCompletion(0);
75 mInButton.setValue(1);
76 mDateEditor.limitToDangerEpoch();
77 mAlertPicker.setGroupFilter(ToneClass.FOREGROUND | ToneClass.CUSTOM);
78 mAlertPicker.setRights(ToneRights.FULL);
79 // this doesn't work properly yet
80 // mAlertPicker.setCanRecord(true);
81
82 mStatusBox = new StaticTextBox();
83 mStatusBox.setTransparent(true);
84 mStatusBox.setHasBorder(false);
85 mStatusBox.setFont(Font.findSystemFont());
86 mStatusBox.setAutoResize(false);
87
88 mValidateAlarm = new danger.app.Alarm(1, this);
89
90 editAlarm(Alarms.getDefaultAlarm());
91
92 super.onDecoded();
93 }
94
95 protected void editDate(Date date) {
96 // time and date editors must share the same date object
97 mTimeEditor.setDate(date);
98 mDateEditor.setDate(date);
99 mDatePicker.setDate(date);
100 }
101
102 protected void editAlarm(Alarm alarm) {
103 mMessageField.setText(alarm.getMessage());
104
105 int period = alarm.getPeriod();
106 if (period > 0) {
107 for (int i = mPeriodUnitsPopup.itemCount() - 1 ; i >= 0 ; --i) {
108 int unitsPerSec = mPeriodUnitsPopup.getItem(i).getEvent().data;
109 if (period % unitsPerSec == 0) {
110 mPeriodUnitsPopup.setValue(i);
111 mPeriodField.setValue(period / unitsPerSec);
112 break;
113 }
114 }
115 mRepeatCheckBox.setValue(alarm.getRepeating() ? 1 : 0);
116 }
117
118 editDate(new Date(alarm.getDate()));
119
120 boolean usesPeriod = alarm.getUsesPeriod();
121 mInButton.setValue(usesPeriod ? 1 : 0);
122 mAtButton.setValue(usesPeriod ? 0 : 1);
123
124 mAlertPicker.setRingTone(alarm.getAlert());
125 }
126
127 public void editAlarm(Alarm alarm, boolean asNew) {
128 mEditingAlarm = alarm;
129 mAlarmIsNew = asNew;
130 alarm.beginEditing();
131 if (asNew) {
132 mDiscardAlarmButton.hide();
133 } else {
134 editAlarm(alarm);
135 mDiscardAlarmButton.show();
136 }
137 validate();
138 Application.registerForEvent(this, Event.EVENT_TIME_CHANGED);
139 show();
140 if (!asNew) setFocusedDescendant(isIn() ? (View)mPeriodField :
141 (View)mTimeEditor);
142 if (!mHaveShownWindow) {
143 Rect alarmRect = mSetAlarmButton.getFrame();
144 Rect contentRect = getContentRect();
145 mStatusBox.setPosition(contentRect.left + 8, alarmRect.top + 2);
146 mStatusBox.setSize(alarmRect.left - 8, 11);
147 mStatusBox.show();
148 addChild(mStatusBox);
149 mHaveShownWindow = true;
150 setAutoSize(false); // don't resize to contain it next time
151 // XXX still a minor potential race here, but it's so unlikely
152 // as to not be worth fixing
153 Application.registerForEvent(this, Event.EVENT_TIME_FORMAT_CHANGED);
154 }
155 }
156
157 public void rescheduleAlarm(Alarm alarm) {
158 setWindowStyle(SYSTEM_DIALOG_STYLE);
159 if (alarm.getRepeating()) {
160 alarm.schedule(); // if we cancel, it'll continue repeating
161 } else {
162 getCancelButton().disable();
163 }
164 editAlarm(alarm, false);
165 }
166
167 protected boolean isIn() {
168 return (mInButton.getValue() == 1);
169 }
170
171 protected int editingInterval() {
172 int itemIndex = mPeriodUnitsPopup.getValue();
173 MenuItem itemAtIndex = mPeriodUnitsPopup.getItem(itemIndex);
174 Event event = itemAtIndex.getEvent();
175 return mPeriodField.getValue() * event.data;
176 }
177
178 protected Date editingDate() {
179 Date date;
180 if (isIn()) {
181 date = new Date();
182 date.addSeconds(editingInterval());
183 } else {
184 date = mDateEditor.getDate();
185 date.setSeconds(0);
186 }
187 return date;
188 }
189
190 protected boolean isValid() {
191 // doesn't redraw properly otherwise
192 invalidate(mStatusBox.getBounds());
193 if (mMessageField.length() == 0) {
194 mStatusBox.setText("Specify a message.");
195 return false;
196 }
197 Date now = new Date();
198 Date editingDate = editingDate();
199 if (isIn()) {
200 mValidateAlarm.resetWake(60 - now.getSeconds());
201 } else {
202 int secondsUntilDate =
203 editingDate.getDangerTimeGMT() - now.getDangerTimeGMT();
204 if (secondsUntilDate <= 0) {
205 mStatusBox.setText("Specify a future date.");
206 return false;
207 }
208 mValidateAlarm.resetWake(secondsUntilDate);
209 }
210 // we read the time from the date editor; if we constrain it to
211 // absolutely now, we won't read an earlier time even if the time
212 // editor sets it
213 now.setTime(0);
214 mDateEditor.min(now);
215 mDatePicker.min(now);
216 mStatusBox.setText(Alarm.dateTimeString(editingDate, true));
217 return true;
218 }
219
220 // XXX make a timer-only version of this which doesn't try to extract
221 // XXX the editing date (it can cause odd behavior by forcing the
222 // XXX NumberEditor to spit out a value)
223 protected void validate() {
224 boolean isValid = isValid();
225 mSetAlarmButton.setEnabled(isValid);
226 if (!isValid)
227 mValidateAlarm.deactivate();
228 }
229
230 protected void stopEditing() {
231 mEditingAlarm = null;
232 mValidateAlarm.deactivate();
233 Application.unregisterForEvent(this, Event.EVENT_TIME_CHANGED);
234 }
235
236 public boolean receiveEvent(Event e) {
237 switch (e.type) {
238 case EVENT_SET_ALARM:
239 if (!isValid())
240 return true;
241 String message = mMessageField.toString();
242 mEditingAlarm.setMessage(message);
243 MessageFinder.usedMessage(message);
244 // set the period regardless, so it gets saved for future use
245 mEditingAlarm.setPeriod(editingInterval(),
246 mRepeatCheckBox.getValue() == 1);
247 if (!isIn()) { // otherwise, date = current time + period
248 mEditingAlarm.setDate(editingDate());
249 }
250 mEditingAlarm.setAlert(mAlertPicker.getRingTone());
251 // schedule before adding so the tooltip is correct
252 mEditingAlarm.schedule();
253 if (mAlarmIsNew) Alarms.addAlarm(mEditingAlarm);
254 Alarms.setDefaultAlarm(mEditingAlarm);
255 stopEditing();
256 return true;
257 case EVENT_CANCEL_ALARM:
258 if (!mAlarmIsNew) mEditingAlarm.resume();
259 stopEditing();
260 return true;
261 case EVENT_DISCARD_ALARM:
262 Alarms.removeAlarm(mEditingAlarm);
263 stopEditing();
264 return true;
265 case Event.EVENT_TIME_FORMAT_CHANGED: // time format changed
266 mTimeEditor.setFormat(DateTimeEditor.FORMAT_TIME_LONG);
267 return true;
268 case Event.EVENT_TIME_CHANGED: // time on device changed
269 case Event.EVENT_ALARM: // computed time invalid
270 case EVENT_VALIDATE: // something changed
271 validate();
272 return true;
273 case EVENT_IN:
274 mAtButton.setValue(0);
275 validate();
276 return true;
277 case EVENT_AT:
278 mInButton.setValue(0);
279 validate();
280 return true;
281 case EVENT_TIME_EDITOR:
282 validate();
283 return true;
284 case EVENT_DATE_EDITOR_TOOK_FOCUS:
285 {
286 Date date = editingDate();
287 Date now = new Date();
288 if (date.compareTo(now) <= 0) { // XXX do with 1 minute fuzz
289 int time = date.getTime();
290 date.set(now); // idiotically, setToday() sets the time too
291 date.setTime(time);
292 if (date.compareTo(now) <= 0)
293 date.addDays(1); // tomorrow
294 editDate(date);
295 }
296 validate();
297 return true;
298 }
299 case EVENT_DATE_EDITOR:
300 mDatePicker.setDate(mDateEditor.getDate());
301 validate();
302 return true;
303 case EVENT_DATE_PICKER:
304 {
305 Date date = (Date)e.argument;
306 date.setTime(mTimeEditor.getDate().getTime());
307 mTimeEditor.setDate(date);
308 mDateEditor.setDate(date);
309 validate();
310 return true;
311 }
312 }
313 return super.receiveEvent(e);
314 }
315}
Note: See TracBrowser for help on using the repository browser.