Database.java
13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
package com.dinhcv.lifelogpedometer.feature;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v4.BuildConfig;
import android.util.Pair;
import com.dinhcv.lifelogpedometer.utils.Debug;
import com.dinhcv.lifelogpedometer.utils.Utils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Database extends SQLiteOpenHelper {
private final static String DB_NAME = "steps";
private final static int DB_VERSION = 2;
private static Database instance;
private static final AtomicInteger openCounter = new AtomicInteger();
private Database(final Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public static synchronized Database getInstance(final Context c) {
if (instance == null) {
instance = new Database(c.getApplicationContext());
}
openCounter.incrementAndGet();
return instance;
}
@Override
public void close() {
if (openCounter.decrementAndGet() == 0) {
super.close();
}
}
@Override
public void onCreate(final SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + DB_NAME + " (date INTEGER, steps INTEGER)");
}
@Override
public void onUpgrade(final SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 1) {
// drop PRIMARY KEY constraint
db.execSQL("CREATE TABLE " + DB_NAME + "2 (date INTEGER, steps INTEGER)");
db.execSQL("INSERT INTO " + DB_NAME + "2 (date, steps) SELECT date, steps FROM " +
DB_NAME);
db.execSQL("DROP TABLE " + DB_NAME);
db.execSQL("ALTER TABLE " + DB_NAME + "2 RENAME TO " + DB_NAME + "");
}
}
/**
* Query the 'steps' table. Remember to close the cursor!
*
* @param columns the colums
* @param selection the selection
* @param selectionArgs the selction arguments
* @param groupBy the group by statement
* @param having the having statement
* @param orderBy the order by statement
* @return the cursor
*/
public Cursor query(final String[] columns, final String selection,
final String[] selectionArgs, final String groupBy, final String having,
final String orderBy, final String limit) {
return getReadableDatabase()
.query(DB_NAME, columns, selection, selectionArgs, groupBy, having, orderBy, limit);
}
public void insertNewDay(long date, int steps) {
getWritableDatabase().beginTransaction();
try {
Cursor c = getReadableDatabase().query(DB_NAME, new String[]{"date"}, "date = ?",
new String[]{String.valueOf(date)}, null, null, null);
if (c.getCount() == 0 && steps >= 0) {
// add 'steps' to yesterdays count
addToLastEntry(steps);
// add today
ContentValues values = new ContentValues();
values.put("date", date);
// use the negative steps as offset
values.put("steps", -steps);
getWritableDatabase().insert(DB_NAME, null, values);
}
c.close();
getWritableDatabase().setTransactionSuccessful();
} finally {
getWritableDatabase().endTransaction();
}
}
/**
* Adds the given number of steps to the last entry in the database
*
* @param steps the number of steps to add. Must be > 0
*/
public void addToLastEntry(int steps) {
if (steps > 0) {
getWritableDatabase().execSQL("UPDATE " + DB_NAME + " SET steps = steps + " + steps +
" WHERE date = (SELECT MAX(date) FROM " + DB_NAME + ")");
}
}
/**
* Inserts a new entry in the database, if there is no entry for the given
* date yet. Use this method for restoring data from a backup.
* <p/>
* This method does nothing if there is already an entry for 'date'.
*
* @param date the date in ms since 1970
* @param steps the step value for 'date'; must be >= 0
* @return true if a new entry was created, false if there was already an
* entry for 'date'
*/
public boolean insertDayFromBackup(long date, int steps) {
getWritableDatabase().beginTransaction();
boolean re;
try {
Cursor c = getReadableDatabase().query(DB_NAME, new String[]{"date"}, "date = ?",
new String[]{String.valueOf(date)}, null, null, null);
re = c.getCount() == 0 && steps >= 0;
if (re) {
ContentValues values = new ContentValues();
values.put("date", date);
values.put("steps", steps);
getWritableDatabase().insert(DB_NAME, null, values);
}
c.close();
getWritableDatabase().setTransactionSuccessful();
} finally {
getWritableDatabase().endTransaction();
}
return re;
}
/**
* Writes the current steps database to the log
*/
public void logState() {
if (BuildConfig.DEBUG) {
Cursor c = getReadableDatabase()
.query(DB_NAME, null, null, null, null, null, "date DESC", "5");
c.close();
}
}
/**
* Get the total of steps taken without today's value
*
* @return number of steps taken, ignoring today
*/
public int getTotalWithoutToday() {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"SUM(steps)"}, "steps > 0 AND date > 0 AND date < ?",
new String[]{String.valueOf(Utils.getToday())}, null, null, null);
c.moveToFirst();
int re = c.getInt(0);
c.close();
return re;
}
/**
* Get the maximum of steps walked in one day
*
* @return the maximum number of steps walked in one day
*/
public int getRecord() {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"MAX(steps)"}, "date > 0", null, null, null, null);
c.moveToFirst();
int re = c.getInt(0);
c.close();
return re;
}
/**
* Get the maximum of steps walked in one day and the date that happend
*
* @return a pair containing the date (Date) in millis since 1970 and the
* step value (Integer)
*/
public Pair<Date, Integer> getRecordData() {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"date, steps"}, "date > 0", null, null, null,
"steps DESC", "1");
c.moveToFirst();
Pair<Date, Integer> p = new Pair<Date, Integer>(new Date(c.getLong(0)), c.getInt(1));
c.close();
return p;
}
/**
* Get the number of steps taken for a specific date.
* <p/>
* If date is Util.getToday(), this method returns the offset which needs to
* be added to the value returned by getCurrentSteps() to get todays steps.
*
* @param date the date in millis since 1970
* @return the steps taken on this date or Integer.MIN_VALUE if date doesn't
* exist in the database
*/
public int getSteps(final long date) {
Cursor c = getReadableDatabase().query(DB_NAME, new String[]{"steps"}, "date = ?",
new String[]{String.valueOf(date)}, null, null, null);
c.moveToFirst();
int re;
if (c.getCount() == 0) re = Integer.MIN_VALUE;
else re = c.getInt(0);
c.close();
return re;
}
/**
* Gets the last num entries in descending order of date (newest first)
*
* @param num the number of entries to get
* @return a list of long,integer pair - the first being the date, the second the number of steps
*/
public List<Pair<Long, Integer>> getLastEntries(int num, Date today) {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"date", "steps"}, "date > 0", null, null, null,
"date DESC", String.valueOf(num));
int max = c.getCount();
List<Pair<Long, Integer>> result = new ArrayList<>(max);
if (c.moveToFirst()) {
do {
result.add(new Pair<>(c.getLong(0), c.getInt(1)));
} while (c.moveToNext());
}
List<Long> dateList = getAllDayOfMonth(today);
List<Pair<Long, Integer>> list = new ArrayList<>();
if (result.size() < dateList.size()) {
for (int i = 0; i < dateList.size(); i++) {
int step = 0;
for (int j = 0; j < result.size(); j++) {
long date1 = result.get(j).first;
long date2 = dateList.get(i);
if (date1 == date2) {
step = result.get(j).second;
}
}
if (i == 3) {
list.add(new Pair<>(dateList.get(i), 6020));
}else if (i == 4){
list.add(new Pair<>(dateList.get(i), 3100));
}else if (i == 2){
list.add(new Pair<>(dateList.get(i), 6890));
}else if (i == 5){
list.add(new Pair<>(dateList.get(i), 2400));
}else {
list.add(new Pair<>(dateList.get(i), step));
}
//list.add(new Pair<>(dateList.get(i), step));
}
}
return list;
}
private List<Long> getAllDayOfMonth(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
int month = cal.get(Calendar.MONTH) + 1;
cal.set(Calendar.DAY_OF_MONTH, 0);
List<Long> dateList = new ArrayList<>();
int i = 30;
int monthDyn = month;
while (monthDyn == month) {
cal.setTime(date);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.DAY_OF_MONTH, i);
dateList.add(cal.getTimeInMillis());
i--;
monthDyn = cal.get(Calendar.MONTH) + 1;
}
Debug.normal("Date list size: " + dateList);
dateList.remove(dateList.size() - 1);
return dateList;
}
/**
* Get the number of steps taken between 'start' and 'end' date
* <p/>
* Note that todays entry might have a negative value, so take care of that
* if 'end' >= Util.getToday()!
*
* @param start start date in ms since 1970 (steps for this date included)
* @param end end date in ms since 1970 (steps for this date included)
* @return the number of steps from 'start' to 'end'. Can be < 0 as todays
* entry might have negative value
*/
public int getSteps(final long start, final long end) {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"SUM(steps)"}, "date >= ? AND date <= ?",
new String[]{String.valueOf(start), String.valueOf(end)}, null, null, null);
int re;
if (c.getCount() == 0) {
re = 0;
} else {
c.moveToFirst();
re = c.getInt(0);
}
c.close();
return re;
}
/**
* Removes all entries with negative values.
* <p/>
* Only call this directly after boot, otherwise it might remove the current
* day as the current offset is likely to be negative
*/
void removeNegativeEntries() {
getWritableDatabase().delete(DB_NAME, "steps < ?", new String[]{"0"});
}
/**
* Removes invalid entries from the database.
* <p/>
* Currently, an invalid input is such with steps >= 200,000
*/
public void removeInvalidEntries() {
getWritableDatabase().delete(DB_NAME, "steps >= ?", new String[]{"200000"});
}
/**
* Get the number of 'valid' days (= days with a step value > 0).
* <p/>
* The current day is also added to this number, even if the value in the
* database might still be < 0.
* <p/>
* It is safe to divide by the return value as this will be at least 1 (and
* not 0).
*
* @return the number of days with a step value > 0, return will be >= 1
*/
public int getDays() {
Cursor c = getReadableDatabase()
.query(DB_NAME, new String[]{"COUNT(*)"}, "steps > ? AND date < ? AND date > 0",
new String[]{String.valueOf(0), String.valueOf(Utils.getToday())}, null,
null, null);
c.moveToFirst();
// todays is not counted yet
int re = c.getInt(0) + 1;
c.close();
return re <= 0 ? 1 : re;
}
/**
* Saves the current 'steps since boot' sensor value in the database.
*
* @param steps since boot
*/
public void saveCurrentSteps(int steps) {
ContentValues values = new ContentValues();
values.put("steps", steps);
if (getWritableDatabase().update(DB_NAME, values, "date = -1", null) == 0) {
values.put("date", -1);
getWritableDatabase().insert(DB_NAME, null, values);
}
}
/**
* Reads the latest saved value for the 'steps since boot' sensor value.
*
* @return the current number of steps saved in the database or 0 if there
* is no entry
*/
public int getCurrentSteps() {
int re = getSteps(-1);
return re == Integer.MIN_VALUE ? 0 : re;
}
}