package net.sabi.pester; import danger.app.Application; import danger.app.Event; import danger.audio.ToneClass; import danger.audio.ToneRights; import danger.internal.Date; import danger.ui.Button; import danger.ui.CheckBox; import danger.ui.Color; import danger.ui.DateTimeEditor; import danger.ui.DatePicker; import danger.ui.DialogWindow; import danger.ui.Font; import danger.ui.Layout; import danger.ui.MenuItem; import danger.ui.Pen; import danger.ui.PopupMenu; import danger.ui.RadioButton; import danger.ui.Rect; import danger.ui.RingTonePicker; import danger.ui.StaticTextBox; import danger.ui.TypeAheadTextField; import danger.ui.View; import danger.util.DEBUG; public class AlarmSetDialog extends DialogWindow implements Resources, Commands { private TypeAheadTextField mMessageField; private RadioButton mInButton, mAtButton; private EventfulNumberEditor mPeriodField; private PopupMenu mPeriodUnitsPopup; private CheckBox mRepeatCheckBox; private DateTimeEditor mTimeEditor, mDateEditor; private DatePicker mDatePicker; private RingTonePicker mAlertPicker; private Button mDiscardAlarmButton, mSetAlarmButton; private StaticTextBox mStatusBox; private Alarm mEditingAlarm; private boolean mAlarmIsNew; private boolean mHaveShownWindow = false; private danger.app.Alarm mValidateAlarm; public static AlarmSetDialog getDialog() { AlarmSetDialog dialog = (AlarmSetDialog) Application.getCurrentApp().getDialog(ID_ALARM_SET_DIALOG, null); return dialog; } public void onDecoded() { setCancelButtonEvent(new Event(this, EVENT_CANCEL_ALARM)); mMessageField = (TypeAheadTextField)getDescendantWithID(ID_MESSAGE_FIELD); mInButton = (RadioButton)getDescendantWithID(ID_IN_BUTTON); mAtButton = (RadioButton)getDescendantWithID(ID_AT_BUTTON); mPeriodField = (EventfulNumberEditor)getDescendantWithID(ID_PERIOD_FIELD); mPeriodUnitsPopup = (PopupMenu)getDescendantWithID(ID_PERIOD_UNITS_POPUP); mRepeatCheckBox = (CheckBox)getDescendantWithID(ID_PERIOD_REPEAT_CHECKBOX); mTimeEditor = (DateTimeEditor)getDescendantWithID(ID_TIME_EDITOR); mDateEditor = (DateTimeEditor)getDescendantWithID(ID_DATE_EDITOR); mDatePicker = (DatePicker)getDescendantWithID(ID_DATE_PICKER); mAlertPicker = (RingTonePicker)getDescendantWithID(ID_ALERT_PICKER); mSetAlarmButton = (Button)getDescendantWithID(ID_SET_ALARM_BUTTON); mDiscardAlarmButton = (Button)getDescendantWithID(ID_DISCARD_ALARM_BUTTON); mMessageField.setTextFinder(new MessageFinder()); mMessageField.setNumCharsNeededForCompletion(0); mInButton.setValue(1); mPeriodField.mAlphaView = mPeriodUnitsPopup; mDateEditor.limitToDangerEpoch(); mAlertPicker.setGroupFilter(ToneClass.FOREGROUND | ToneClass.CUSTOM); mAlertPicker.setRights(ToneRights.FULL); // this doesn't work properly yet // mAlertPicker.setCanRecord(true); mStatusBox = new StaticTextBox(); mStatusBox.setTransparent(true); mStatusBox.setHasBorder(false); mStatusBox.setFont(Font.findSystemFont()); mStatusBox.setAutoResize(false); mValidateAlarm = new danger.app.Alarm(1, this); editAlarm(Alarms.getDefaultAlarm()); super.onDecoded(); } protected void editDate(Date date) { // time and date editors must share the same date object mTimeEditor.setDate(date); mDateEditor.setDate(date); mDatePicker.setDate(date); } protected void editAlarm(Alarm alarm) { mMessageField.setText(alarm.getMessage()); int period = alarm.getPeriod(); if (period > 0) { for (int i = mPeriodUnitsPopup.itemCount() - 1 ; i >= 0 ; --i) { int unitsPerSec = mPeriodUnitsPopup.getItem(i).getEvent().data; if (period % unitsPerSec == 0) { mPeriodUnitsPopup.setValue(i); mPeriodField.setValue(period / unitsPerSec); break; } } mRepeatCheckBox.setValue(alarm.getRepeating() ? 1 : 0); } editDate(new Date(alarm.getDate())); boolean usesPeriod = alarm.getUsesPeriod(); mInButton.setValue(usesPeriod ? 1 : 0); mAtButton.setValue(usesPeriod ? 0 : 1); mAlertPicker.setRingTone(alarm.getAlert()); } public void editAlarm(Alarm alarm, boolean asNew) { mEditingAlarm = alarm; mAlarmIsNew = asNew; alarm.beginEditing(); if (asNew) { mDiscardAlarmButton.hide(); } else { editAlarm(alarm); mDiscardAlarmButton.show(); } validate(); Application.registerForEvent(this, Event.EVENT_TIME_CHANGED); show(); if (!asNew) setFocusedDescendant(isIn() ? (View)mPeriodField : (View)mTimeEditor); if (!mHaveShownWindow) { Rect alarmRect = mSetAlarmButton.getFrame(); Rect contentRect = getContentRect(); mStatusBox.setPosition(contentRect.left + 8, alarmRect.top + 2); mStatusBox.setSize(alarmRect.left - 8, 11); mStatusBox.show(); addChild(mStatusBox); mHaveShownWindow = true; setAutoSize(false); // don't resize to contain it next time // XXX still a minor potential race here, but it's so unlikely // as to not be worth fixing Application.registerForEvent(this, Event.EVENT_TIME_FORMAT_CHANGED); } } public void rescheduleAlarm(Alarm alarm) { setWindowStyle(SYSTEM_DIALOG_STYLE); if (alarm.getRepeating()) { alarm.schedule(); // if we cancel, it'll continue repeating } else { getCancelButton().disable(); } editAlarm(alarm, false); } protected boolean isIn() { return (mInButton.getValue() == 1); } protected int editingInterval() { int itemIndex = mPeriodUnitsPopup.getValue(); MenuItem itemAtIndex = mPeriodUnitsPopup.getItem(itemIndex); Event event = itemAtIndex.getEvent(); return mPeriodField.getValue() * event.data; } protected Date editingDate() { Date date; if (isIn()) { date = new Date(); date.addSeconds(editingInterval()); } else { date = mDateEditor.getDate(); date.setSeconds(0); } return date; } protected boolean isValid() { // doesn't redraw properly otherwise invalidate(mStatusBox.getBounds()); if (mMessageField.length() == 0) { mStatusBox.setText("Specify a message."); return false; } Date now = new Date(); Date editingDate = editingDate(); if (isIn()) { mValidateAlarm.resetWake(60 - now.getSeconds()); } else { int secondsUntilDate = editingDate.getDangerTimeGMT() - now.getDangerTimeGMT(); if (secondsUntilDate <= 0) { mStatusBox.setText("Specify a future date."); return false; } mValidateAlarm.resetWake(secondsUntilDate); } // we read the time from the date editor; if we constrain it to // absolutely now, we won't read an earlier time even if the time // editor sets it now.setTime(0); mDateEditor.min(now); mDatePicker.min(now); mStatusBox.setText(Alarm.dateTimeString(editingDate, true)); return true; } // XXX make a timer-only version of this which doesn't try to extract // XXX the editing date (it can cause odd behavior by forcing the // XXX NumberEditor to spit out a value) protected void validate() { boolean isValid = isValid(); mSetAlarmButton.setEnabled(isValid); if (!isValid) mValidateAlarm.deactivate(); } protected void stopEditing() { mEditingAlarm = null; mValidateAlarm.deactivate(); Application.unregisterForEvent(this, Event.EVENT_TIME_CHANGED); } public boolean receiveEvent(Event e) { switch (e.type) { case EVENT_SET_ALARM: if (!isValid()) return true; String message = mMessageField.toString(); mEditingAlarm.setMessage(message); MessageFinder.usedMessage(message); // set the period regardless, so it gets saved for future use mEditingAlarm.setPeriod(editingInterval(), mRepeatCheckBox.getValue() == 1); if (!isIn()) { // otherwise, date = current time + period mEditingAlarm.setDate(editingDate()); } mEditingAlarm.setAlert(mAlertPicker.getRingTone()); // schedule before adding so the tooltip is correct mEditingAlarm.schedule(); if (mAlarmIsNew) Alarms.addAlarm(mEditingAlarm); Alarms.setDefaultAlarm(mEditingAlarm); stopEditing(); return true; case EVENT_CANCEL_ALARM: if (!mAlarmIsNew) mEditingAlarm.resume(); stopEditing(); return true; case EVENT_DISCARD_ALARM: Alarms.removeAlarm(mEditingAlarm); stopEditing(); return true; case Event.EVENT_TIME_FORMAT_CHANGED: // time format changed mTimeEditor.setFormat(DateTimeEditor.FORMAT_TIME_LONG); return true; case Event.EVENT_TIME_CHANGED: // time on device changed case Event.EVENT_ALARM: // computed time invalid case EVENT_VALIDATE: // something changed validate(); return true; case EVENT_IN: mAtButton.setValue(0); validate(); return true; case EVENT_AT: mInButton.setValue(0); validate(); return true; case EVENT_TIME_EDITOR: validate(); return true; case EVENT_DATE_EDITOR_TOOK_FOCUS: { Date date = editingDate(); Date now = new Date(); if (date.compareTo(now) <= 0) { // XXX do with 1 minute fuzz int time = date.getTime(); date.set(now); // idiotically, setToday() sets the time too date.setTime(time); if (date.compareTo(now) <= 0) date.addDays(1); // tomorrow editDate(date); } validate(); return true; } case EVENT_DATE_EDITOR: mDatePicker.setDate(mDateEditor.getDate()); validate(); return true; case EVENT_DATE_PICKER: { Date date = (Date)e.argument; date.setTime(mTimeEditor.getDate().getTime()); mTimeEditor.setDate(date); mDateEditor.setDate(date); validate(); return true; } } return super.receiveEvent(e); } }