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

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

Make date constraints behave predictably. Fix flashing Set button when typing message while date is invalid.

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