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.ActiveListView; import danger.ui.Button; import danger.ui.CheckBox; import danger.ui.DateTimeEditor; import danger.ui.DatePicker; import danger.ui.DialogWindow; import danger.ui.Menu; import danger.ui.MenuItem; import danger.ui.NumberEditor; import danger.ui.PopupMenu; import danger.ui.RadioButton; import danger.ui.Rect; import danger.ui.RingTonePicker; import danger.ui.TypeAheadTextField; import danger.util.ActiveList; import danger.util.StdActiveList; import danger.util.DEBUG; public class AlarmListView extends ActiveListView implements Resources, Commands { private static Pester sPester; private static AlarmListWindow sAlarmListWindow; // XXX some of these shouldn't conceptually be static, but who cares private static StdActiveList sAlarmList; private static Alarms sAlarms; private static DialogWindow sEditWindow; private TypeAheadTextField sMessageField; private RadioButton sInButton, sAtButton; private NumberEditor sPeriodField; private Menu sPeriodUnitsMenu; private PopupMenu sPeriodUnitsPopup; // XXX don't allow <5 second repeating alarms private CheckBox sRepeatCheckBox; private DateTimeEditor sTimeEditor, sDateEditor; private DatePicker sDatePicker; private RingTonePicker sAlertPicker; private Button sDiscardAlarmButton, sSetAlarmButton; private static Alarm sFocusedAlarm, sEditingAlarm; private static final int EVENT_CANCEL_ALARM = -1; // XXX move this? public void onDecoded() { sPester = (Pester)Application.getCurrentApp(); sAlarmListWindow = (AlarmListWindow)getWindow(); sAlarmList = new StdActiveList(); setList(sAlarmList); sEditWindow = sPester.getDialog(ID_ALARM_SET_DIALOG, this); sEditWindow.setCancelButtonEvent(new Event(this, EVENT_CANCEL_ALARM)); sMessageField = (TypeAheadTextField)sEditWindow.getDescendantWithID(ID_MESSAGE_FIELD); sEditWindow.disableBottomRightButtonOnEmptyField(sMessageField); sInButton = (RadioButton)sEditWindow.getDescendantWithID(ID_IN_BUTTON); sAtButton = (RadioButton)sEditWindow.getDescendantWithID(ID_AT_BUTTON); sPeriodField = (NumberEditor)sEditWindow.getDescendantWithID(ID_PERIOD_FIELD); sPeriodUnitsMenu = (Menu)sEditWindow.getDescendantWithID(ID_PERIOD_UNITS_MENU); sPeriodUnitsPopup = (PopupMenu)sEditWindow.getDescendantWithID(ID_PERIOD_UNITS_POPUP); sRepeatCheckBox = (CheckBox)sEditWindow.getDescendantWithID(ID_PERIOD_REPEAT_CHECKBOX); sTimeEditor = (DateTimeEditor)sEditWindow.getDescendantWithID(ID_TIME_EDITOR); sDateEditor = (DateTimeEditor)sEditWindow.getDescendantWithID(ID_DATE_EDITOR); sDatePicker = (DatePicker)sEditWindow.getDescendantWithID(ID_DATE_PICKER); sAlertPicker = (RingTonePicker)sEditWindow.getDescendantWithID(ID_ALERT_PICKER); sSetAlarmButton = (Button)sEditWindow.getDescendantWithID(ID_SET_ALARM_BUTTON); sDiscardAlarmButton = (Button)sEditWindow.getDescendantWithID(ID_DISCARD_ALARM_BUTTON); sInButton.setValue(1); sDateEditor.limitToDangerEpoch(); sAlertPicker.setGroupFilter(ToneClass.FOREGROUND | ToneClass.BACKGROUND | ToneClass.CUSTOM); sAlertPicker.setRights(ToneRights.FULL); sAlertPicker.setCanRecord(true); super.onDecoded(); } public boolean eventWidgetUp(int widget, Event e) { switch (widget) { case Event.DEVICE_BUTTON_BACK: case Event.DEVICE_BUTTON_CANCEL: sPester.returnToLauncher(); return true; } return super.eventWidgetUp(widget, e); } protected void editDateOrPeriod(Alarm alarm) { boolean usesPeriod = alarm.getUsesPeriod(); if (usesPeriod) { int period = alarm.getPeriod(); for (int i = sPeriodUnitsPopup.itemCount() - 1 ; i >= 0 ; --i) { MenuItem item = sPeriodUnitsPopup.getItem(i); int unitsPerSec = item.getEvent().data; if (period % unitsPerSec == 0) { sPeriodUnitsPopup.setValueWithItemID(sPeriodUnitsMenu.getIDOfItem(item)); sPeriodField.setValue(period / unitsPerSec); break; } } sRepeatCheckBox.setValue(alarm.getRepeating() ? 1 : 0); } Date date = alarm.getDate(); sTimeEditor.setDate(date); sDateEditor.setDate(date); sDatePicker.setDate(date); sInButton.setValue(usesPeriod ? 1 : 0); sAtButton.setValue(usesPeriod ? 0 : 1); } protected void editAlarm(Alarm alarm, boolean asNew) { sEditingAlarm = alarm; alarm.beginEditing(); sMessageField.setText(alarm.getMessage()); if (asNew) { sDiscardAlarmButton.hide(); } else { sDiscardAlarmButton.show(); editDateOrPeriod(alarm); } constrainDate(); sAlertPicker.setRingTone(sEditingAlarm.getAlert()); sEditWindow.show(); } protected Date editingDate() { Date date = sDateEditor.getDate(); date.setTime(sTimeEditor.getDate().getTime()); date.setSeconds(0); return date; } protected void constrainDate() { // XXX schedule every minute (second?) if in AT mode // XXX could use onPeriodicPulse() perhaps Date now = new Date(); sDateEditor.min(now); sDatePicker.min(now); sSetAlarmButton.setEnabled(sInButton.getValue() == 1 || editingDate().compareTo(now) > 0); } public boolean receiveEvent(Event e) { switch (e.type) { case EVENT_NEW_ALARM: editAlarm(new Alarm(), true); return true; case EVENT_SET_ALARM: sEditingAlarm.setMessage(sMessageField.toString()); if (sInButton.getValue() == 1) { int itemID = sPeriodUnitsPopup.getValueAsItemID(); MenuItem itemWithID = sPeriodUnitsMenu.getItemWithID(itemID); Event event = itemWithID.getEvent(); sEditingAlarm.setPeriod(sPeriodField.getValue() * event.data, sRepeatCheckBox.getValue() == 1); } else { sEditingAlarm.setDate(editingDate()); } sEditingAlarm.setAlert(sAlertPicker.getRingTone()); // schedule before adding so the tooltip is correct sEditingAlarm.schedule(); if (sEditingAlarm != sFocusedAlarm) { // new alarm sAlarmList.addItem(sEditingAlarm); setFocusedItem(sEditingAlarm); sFocusedAlarm = sEditingAlarm; } return true; case EVENT_CANCEL_ALARM: if (sEditingAlarm == sFocusedAlarm) // existing alarm sEditingAlarm.resume(); sEditingAlarm = null; return true; case EVENT_DISCARD_ALARM: // XXX confirmation sAlarmList.removeItem(sFocusedAlarm); return true; case EVENT_IN: sAtButton.setValue(0); constrainDate(); return true; case EVENT_AT: sInButton.setValue(0); constrainDate(); return true; case EVENT_TIME_EDITOR: Date date = editingDate(); // XXX do this on exit from the field, not on change if (date.compareTo(new Date()) <= 0) { date.addDays(1); sDatePicker.setDate(date); sDateEditor.setDate(date); } constrainDate(); return true; case EVENT_DATE_EDITOR: sDatePicker.setDate(sDateEditor.getDate()); constrainDate(); return true; case EVENT_DATE_PICKER: sDateEditor.setDate((Date)e.argument); constrainDate(); return true; } return super.receiveEvent(e); } protected void itemActivated(Object item) { editAlarm(sFocusedAlarm, false); } protected void itemFocused(Object item) { sFocusedAlarm = (Alarm)item; } public int alarmsSet() { return getListSize(); } public void onItemAdded(ActiveList list, Object item, int index) { sAlarmListWindow.updateAlarmCount(); super.onItemAdded(list, item, index); } public void onItemRemoved(ActiveList list, Object item, int index) { ((Alarm)item).cancel(); sAlarmListWindow.updateAlarmCount(); super.onItemRemoved(list, item, index); } protected CharSequence getToolTipForItem(Object item) { if (item == null) // this is dumb, why do we get asked for a tooltip? return null; // XXX need "in XXX minutes" too return ((Alarm)item).getDateTimeString(); } }