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

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

If you're editing an alarm, focus on the time/interval edit field
rather than the message. Relayout set alarm display when time/date
format changes (yay positionDynamically!). Attempt to fix tooltip for

1-minute alarm refreshing/flashing without changing (needs more

testing). Build against 3.0 libraries by default.

File size: 9.1 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 mInButton.setValue(1);
74 mDateEditor.limitToDangerEpoch();
75 mAlertPicker.setGroupFilter(ToneClass.FOREGROUND | ToneClass.CUSTOM);
76 mAlertPicker.setRights(ToneRights.FULL);
77 // this doesn't work properly yet
78 // mAlertPicker.setCanRecord(true);
79
80 mStatusBox = new StaticTextBox();
81 mStatusBox.setTransparent(true);
82 mStatusBox.setHasBorder(false);
83 mStatusBox.setFont(Font.findSystemFont());
84 mStatusBox.setAutoResize(false);
85
86 mValidateAlarm = new danger.app.Alarm(1, this);
87
88 editAlarm(Alarms.getDefaultAlarm());
89
90 super.onDecoded();
91 }
92
93 protected void editDate(Date date) {
94 // time and date editors must share the same date object
95 mTimeEditor.setDate(date);
96 mDateEditor.setDate(date);
97 mDatePicker.setDate(date);
98 }
99
100 protected void editAlarm(Alarm alarm) {
101 mMessageField.setText(alarm.getMessage());
102
103 int period = alarm.getPeriod();
104 if (period > 0) {
105 for (int i = mPeriodUnitsPopup.itemCount() - 1 ; i >= 0 ; --i) {
106 int unitsPerSec = mPeriodUnitsPopup.getItem(i).getEvent().data;
107 if (period % unitsPerSec == 0) {
108 mPeriodUnitsPopup.setValue(i);
109 mPeriodField.setValue(period / unitsPerSec);
110 break;
111 }
112 }
113 mRepeatCheckBox.setValue(alarm.getRepeating() ? 1 : 0);
114 }
115
116 editDate(new Date(alarm.getDate()));
117
118 boolean usesPeriod = alarm.getUsesPeriod();
119 mInButton.setValue(usesPeriod ? 1 : 0);
120 mAtButton.setValue(usesPeriod ? 0 : 1);
121
122 mAlertPicker.setRingTone(alarm.getAlert());
123 }
124
125 public void editAlarm(Alarm alarm, boolean asNew) {
126 mEditingAlarm = alarm;
127 mAlarmIsNew = asNew;
128 alarm.beginEditing();
129 if (asNew) {
130 mDiscardAlarmButton.hide();
131 // XXX default to now?
132 } else {
133 editAlarm(alarm);
134 mDiscardAlarmButton.show();
135 }
136 validate();
137 Application.registerForEvent(this, Event.EVENT_TIME_CHANGED);
138 show();
139 if (!asNew) setFocusedDescendant(isIn() ? (View)mPeriodField :
140 (View)mTimeEditor);
141 if (!mHaveShownWindow) {
142 Rect alarmRect = mSetAlarmButton.getFrame();
143 Rect contentRect = getContentRect();
144 mStatusBox.setPosition(contentRect.left + 8, alarmRect.top + 2);
145 mStatusBox.setSize(alarmRect.left - 8, 11);
146 mStatusBox.show();
147 addChild(mStatusBox);
148 mHaveShownWindow = true;
149 setAutoSize(false); // don't resize to contain it next time
150 // XXX still a minor potential race here, but it's so unlikely
151 // as to not be worth fixing
152 Application.registerForEvent(this, Event.EVENT_TIME_FORMAT_CHANGED);
153 }
154 }
155
156 protected boolean isIn() {
157 return (mInButton.getValue() == 1);
158 }
159
160 protected int editingInterval() {
161 int itemIndex = mPeriodUnitsPopup.getValue();
162 MenuItem itemAtIndex = mPeriodUnitsPopup.getItem(itemIndex);
163 Event event = itemAtIndex.getEvent();
164 return mPeriodField.getValue() * event.data;
165 }
166
167 protected Date editingDate() {
168 Date date;
169 if (isIn()) {
170 date = new Date();
171 date.addSeconds(editingInterval());
172 } else {
173 date = mDateEditor.getDate();
174 date.setSeconds(0);
175 }
176 return date;
177 }
178
179 protected boolean isValid() {
180 // doesn't redraw properly otherwise
181 invalidate(mStatusBox.getBounds());
182 if (mMessageField.length() == 0) {
183 mStatusBox.setText("Specify a message.");
184 return false;
185 }
186 Date now = new Date();
187 Date editingDate = editingDate();
188 if (isIn()) {
189 mValidateAlarm.resetWake(60 - now.getSeconds());
190 } else {
191 int secondsUntilDate =
192 editingDate.getDangerTimeGMT() - now.getDangerTimeGMT();
193 if (secondsUntilDate <= 0) {
194 mStatusBox.setText("Specify a future date.");
195 return false;
196 }
197 mValidateAlarm.resetWake(secondsUntilDate);
198 }
199 // we read the time from the date editor; if we constrain it to
200 // absolutely now, we won't read an earlier time even if the time
201 // editor sets it
202 now.setTime(0);
203 mDateEditor.min(now);
204 mDatePicker.min(now);
205 mStatusBox.setText(Alarm.dateTimeString(editingDate, true));
206 return true;
207 }
208
209 // XXX make a timer-only version of this which doesn't try to extract
210 // XXX the editing date (it can cause odd behavior by forcing the
211 // XXX NumberEditor to spit out a value)
212 protected void validate() {
213 boolean isValid = isValid();
214 mSetAlarmButton.setEnabled(isValid);
215 if (!isValid)
216 mValidateAlarm.deactivate();
217 }
218
219 protected void stopEditing() {
220 mEditingAlarm = null;
221 mValidateAlarm.deactivate();
222 Application.unregisterForEvent(this, Event.EVENT_TIME_CHANGED);
223 }
224
225 public boolean receiveEvent(Event e) {
226 switch (e.type) {
227 case EVENT_SET_ALARM:
228 if (!isValid())
229 return true;
230 mEditingAlarm.setMessage(mMessageField.toString());
231 // set the period regardless, so it gets saved for future use
232 mEditingAlarm.setPeriod(editingInterval(),
233 mRepeatCheckBox.getValue() == 1);
234 if (!isIn()) { // otherwise, date = current time + period
235 mEditingAlarm.setDate(editingDate());
236 }
237 mEditingAlarm.setAlert(mAlertPicker.getRingTone());
238 // schedule before adding so the tooltip is correct
239 mEditingAlarm.schedule();
240 if (mAlarmIsNew) Alarms.addAlarm(mEditingAlarm);
241 Alarms.setDefaultAlarm(mEditingAlarm);
242 stopEditing();
243 return true;
244 case EVENT_CANCEL_ALARM:
245 if (!mAlarmIsNew) mEditingAlarm.resume();
246 stopEditing();
247 return true;
248 case EVENT_DISCARD_ALARM:
249 Alarms.removeAlarm(mEditingAlarm);
250 stopEditing();
251 return true;
252 case Event.EVENT_TIME_FORMAT_CHANGED: // time format changed
253 mTimeEditor.setFormat(DateTimeEditor.FORMAT_TIME_LONG);
254 return true;
255 case Event.EVENT_TIME_CHANGED: // time on device changed
256 case Event.EVENT_ALARM: // computed time invalid
257 case EVENT_VALIDATE: // something changed
258 validate();
259 return true;
260 case EVENT_IN:
261 mAtButton.setValue(0);
262 validate();
263 return true;
264 case EVENT_AT:
265 mInButton.setValue(0);
266 validate();
267 return true;
268 case EVENT_TIME_EDITOR:
269 validate();
270 return true;
271 case EVENT_DATE_EDITOR_TOOK_FOCUS:
272 {
273 Date date = editingDate();
274 Date now = new Date();
275 if (date.compareTo(now) <= 0) { // XXX do with 1 minute fuzz
276 int time = date.getTime();
277 date.set(now); // idiotically, setToday() sets the time too
278 date.setTime(time);
279 if (date.compareTo(now) <= 0)
280 date.addDays(1); // tomorrow
281 editDate(date);
282 }
283 validate();
284 return true;
285 }
286 case EVENT_DATE_EDITOR:
287 mDatePicker.setDate(mDateEditor.getDate());
288 validate();
289 return true;
290 case EVENT_DATE_PICKER:
291 {
292 Date date = (Date)e.argument;
293 date.setTime(mTimeEditor.getDate().getTime());
294 mTimeEditor.setDate(date);
295 mDateEditor.setDate(date);
296 validate();
297 return true;
298 }
299 }
300 return super.receiveEvent(e);
301 }
302}
Note: See TracBrowser for help on using the repository browser.