source: trunk/hiptop/pester/net/sabi/pester/Alarms.java@ 299

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

Midnight check - changes "Tomorrow" to "Today" etc. at midnight.

File size: 6.5 KB
Line 
1package net.sabi.pester;
2
3import java.util.Arrays;
4import java.util.Comparator;
5import danger.app.Application;
6import danger.app.DataStore;
7import danger.app.Event;
8import danger.app.SettingsDB;
9import danger.app.SettingsDBException;
10import danger.internal.Date;
11import danger.util.ByteArray;
12import danger.util.StdActiveList;
13import danger.util.DEBUG;
14
15public class Alarms extends StdActiveList {
16 // max # records in a datastore
17 public static final int MAX_ALARM_COUNT = 50;
18
19 private static Alarms sAlarmList = null;
20 private static Listener sListener;
21 private static SettingsDB sSettingsDB;
22 private static DataStore sDeletedAlarms;
23
24 private DataStore mDataStore;
25
26 private Alarms() {
27 mDataStore = DataStore.createDataStore("alarms", true /* auto sync */);
28 // register us for Event.EVENT_DATASTORE_RESTORED, which only
29 // seems to be documented at:
30 // <http://developer.danger.com/forum/index.php?t=msg&th=27>
31 mDataStore.setAutoSyncNotifyee(sListener);
32 refreshFromDataStore(false);
33 }
34
35 private int[] deletedAlarmsCreationIDs() {
36 byte[][] deletedAlarmsData = sDeletedAlarms.getRecords();
37 int[] ids = new int[deletedAlarmsData.length];
38 for (int i = 0 ; i < deletedAlarmsData.length ; ++i)
39 ids[i] = ByteArray.readInt(deletedAlarmsData[i], 0);
40 Arrays.sort(ids);
41 return ids;
42 }
43
44 private void dumpAlarms() { // XXX enable via IPC, make validity checking
45 int i;
46 DEBUG.p("== ALARMS ==");
47 for (i = 0 ; i < size() ; ++i) {
48 DEBUG.p(((Alarm)getItem(i)).description());
49 }
50
51 }
52 private void dumpDatastore() {
53 int i;
54 DEBUG.p("== DATASTORE CONTENTS ==");
55 byte[][] alarmsData = mDataStore.getRecords();
56 for (i = 0 ; i < alarmsData.length ; ++i) {
57 Alarm alarm = new Alarm();
58 alarm.fromByteArray(alarmsData[i]);
59 alarm.setUID(mDataStore.getRecordUID(i));
60 DEBUG.p(alarm.description());
61 }
62 }
63 void refreshFromDataStore(boolean datastoreRestored) {
64 sAlarmList = null;
65 int[] deleted = null;
66 if (datastoreRestored) {
67 deleted = deletedAlarmsCreationIDs();
68 DEBUG.p("+++ BEFORE RESOLUTION +++");
69 dumpAlarms();
70 dumpDatastore();
71 }
72 removeAllItems(); // XXX no, we can't do this because some alarms may be (a) in the process of being edited, (b) sitting at expiry, or (c) are periodic or snoozed and need their absolute time preserved. Instead, we should just add all the new records - but what about conflicts?
73 if (datastoreRestored)
74 mDataStore.doneResolvingConflict(); // renumbers on-device records
75 if (datastoreRestored) {
76 DEBUG.p("+++ AFTER RESOLUTION +++");
77 dumpDatastore();
78 }
79 byte[][] alarmsData = mDataStore.getRecords();
80 int i;
81 for (i = 0 ; i < alarmsData.length ; ++i) {
82 Alarm alarm = new Alarm();
83 alarm.fromByteArray(alarmsData[i]);
84 alarm.setUID(mDataStore.getRecordUID(i));
85 if (Arrays.binarySearch(deleted, alarm.getCreationID()) >= 0) {
86 mDataStore.removeRecord(i);
87 continue;
88 }
89 insertItemSorted(alarm, alarm);
90 alarm.resume();
91 }
92 try {
93 MessageFinder.setMessageListFromByteArray(sSettingsDB.getBytes(KEY_RECENT_MESSAGES));
94 } catch (SettingsDBException e) {
95 MessageFinder.setDefaultMessageList();
96 }
97 DEBUG.p("+++ AFTER RESTORATION +++");
98 dumpAlarms();
99 dumpDatastore();
100 sAlarmList = this;
101 }
102
103 public static Alarms getList() {
104 if (sAlarmList == null) {
105 sSettingsDB = new SettingsDB("settings", true /* auto sync */);
106 sDeletedAlarms = DataStore.createDataStore("deleted alarms");
107 sListener = new Listener();
108 new Alarms();
109 Application.registerForEvent(sListener, Event.EVENT_TIME_CHANGED);
110 }
111 return sAlarmList;
112 }
113
114 public static boolean canCreateAlarm() {
115 return (sAlarmList.size() < MAX_ALARM_COUNT);
116 }
117 public static void addAlarm(Alarm alarm) {
118 sAlarmList.insertItemSorted(alarm, alarm);
119 }
120 public static void removeAlarm(Alarm alarm) {
121 sAlarmList.removeItem(alarm);
122 }
123 public static void recentMessagesChanged() {
124 sSettingsDB.setBytes(KEY_RECENT_MESSAGES,
125 MessageFinder.messageListAsByteArray());
126 }
127
128 protected void onItemAdded(Object item, int index) {
129 if (sAlarmList == null) // restoring from service
130 return;
131 Alarm alarm = (Alarm)item;
132 index = mDataStore.addRecord(alarm.toByteArray());
133 alarm.setUID(mDataStore.getRecordUID(index));
134 DEBUG.p("ADD" + alarm.description());
135 }
136
137 protected void onItemRemoved(Object item, int index) {
138 if (sAlarmList == null) // restoring from service (after hard reset)
139 return;
140 Alarm alarm = (Alarm)item;
141 int uid = alarm.getUID();
142 if (uid == 0)
143 return;
144 mDataStore.removeRecordByUID(uid);
145 if (uid < 0)
146 mDataStore.removeRecordByUID(-uid);
147 while (sDeletedAlarms.getRecordCount() >= MAX_ALARM_COUNT)
148 sDeletedAlarms.removeRecord(0);
149 byte[] idBytes = new byte[4];
150 ByteArray.writeInt(idBytes, 0, alarm.getCreationID());
151 sDeletedAlarms.addRecord(idBytes);
152 DEBUG.p("DEL" + alarm.description());
153 }
154
155 public void onItemUpdated(Object item, int index) {
156 if (sAlarmList == null) // restoring from service
157 return;
158 Alarm alarm = (Alarm)item;
159 mDataStore.setRecordDataByUID(alarm.getUID(), alarm.toByteArray(), true);
160 DEBUG.p("MOD" + alarm.description());
161 }
162
163 private static String KEY_DEFAULT_ALARM = "default alarm";
164 private static String KEY_RECENT_MESSAGES = "recent messages";
165
166 public static Alarm getDefaultAlarm() {
167 Alarm defaultAlarm = new Alarm();
168 try {
169 defaultAlarm.fromByteArray(sSettingsDB.getBytes(KEY_DEFAULT_ALARM));
170 } catch (SettingsDBException e) {
171 defaultAlarm = new Alarm();
172 defaultAlarm.setDate(new Date());
173 defaultAlarm.setPeriod(600, false);
174 }
175 return defaultAlarm;
176 }
177 public static void setDefaultAlarm(Alarm alarm) {
178 sSettingsDB.setBytes(KEY_DEFAULT_ALARM, alarm.toByteArray());
179 }
180
181 static class Listener extends danger.app.Listener
182 implements danger.util.ActiveList.ForEach {
183
184 public void receive(Object item) {
185 ((Alarm)item).timeChanged();
186 }
187
188 public boolean receiveEvent(Event e) {
189 if (e.type == Event.EVENT_TIME_CHANGED) {
190 Alarms.getList().forEach(this);
191 ((Pester)Application.getCurrentApp()).resetMidnightCheck();
192 return true;
193 } else if (e.type == Event.EVENT_DATASTORE_RESTORED) {
194 String dbName = (String)e.argument;
195 DEBUG.p("Pester: DATASTORE_RESTORED " + dbName);
196 if (dbName.endsWith("alarms")) {
197 // because we only get/set the default alarm on demand
198 // there's no need to handle conflicts with the settings...
199 // XXX but what happens if we set a (default) alarm, then
200 // the SettingsDB restores?
201 Alarms.getList().refreshFromDataStore(true);
202 }
203 }
204 return super.receiveEvent(e);
205 }
206 }
207}
Note: See TracBrowser for help on using the repository browser.