Files
calendar-lite/android/org/gingercal/Db.java
T
2026-10-15 16:35:05 +02:00

194 lines
7.5 KiB
Java

package org.gingercal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import org.gingercal.Model.Cal;
import org.gingercal.Model.Ev;
/** Local cache so the agenda works offline and the phone doesn't refetch on every launch. */
public class Db extends SQLiteOpenHelper implements Model.Store {
private static Db instance;
public static synchronized Db get(Context c) {
if (instance == null) instance = new Db(c.getApplicationContext());
return instance;
}
private Db(Context c) {
super(c, "gingercal.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE cals (href TEXT PRIMARY KEY, name TEXT, color INTEGER, enabled INTEGER,"
+ " writable INTEGER, ctag TEXT, synced INTEGER)");
db.execSQL("CREATE TABLE events (_id INTEGER PRIMARY KEY AUTOINCREMENT, cal TEXT, href TEXT, etag TEXT,"
+ " uid TEXT, title TEXT, location TEXT, descr TEXT, dtstart INTEGER, dtend INTEGER,"
+ " allday INTEGER, recurring INTEGER)");
db.execSQL("CREATE INDEX ev_start ON events(dtstart)");
db.execSQL("CREATE INDEX ev_cal ON events(cal)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int from, int to) {
db.execSQL("DROP TABLE IF EXISTS events");
db.execSQL("DROP TABLE IF EXISTS cals");
onCreate(db);
}
// ------------------------------------------------------------ calendars
public synchronized Map<String, Cal> calendars() {
Map<String, Cal> m = new LinkedHashMap<String, Cal>();
Cursor c = getReadableDatabase().rawQuery(
"SELECT href, name, color, enabled, writable, ctag, synced FROM cals ORDER BY name", null);
try {
while (c.moveToNext()) {
Cal x = new Cal();
x.href = c.getString(0);
x.name = c.getString(1);
x.color = c.getInt(2);
x.enabled = c.getInt(3) != 0;
x.writable = c.getInt(4) != 0;
x.ctag = c.getString(5);
x.synced = c.getLong(6);
m.put(x.href, x);
}
} finally {
c.close();
}
return m;
}
public List<Cal> calendarList() {
return new ArrayList<Cal>(calendars().values());
}
public synchronized void saveCalendar(Cal c) {
getWritableDatabase().execSQL("INSERT OR REPLACE INTO cals (href, name, color, enabled, writable, ctag, synced)"
+ " VALUES (?,?,?,?,?,?,?)", new Object[] {c.href, c.name, Integer.valueOf(c.color),
Integer.valueOf(c.enabled ? 1 : 0), Integer.valueOf(c.writable ? 1 : 0), c.ctag, Long.valueOf(c.synced)});
}
public synchronized void setEnabled(String href, boolean on) {
// reset ctag/synced so the next sync refetches it
getWritableDatabase().execSQL("UPDATE cals SET enabled=?, ctag='', synced=0 WHERE href=?",
new Object[] {Integer.valueOf(on ? 1 : 0), href});
if (!on) getWritableDatabase().execSQL("DELETE FROM events WHERE cal=?", new Object[] {href});
}
public synchronized void deleteCalendar(String href) {
SQLiteDatabase db = getWritableDatabase();
db.execSQL("DELETE FROM events WHERE cal=?", new Object[] {href});
db.execSQL("DELETE FROM cals WHERE href=?", new Object[] {href});
}
public synchronized void forgetSyncState() {
getWritableDatabase().execSQL("UPDATE cals SET ctag='', synced=0");
}
public synchronized void clearAll() {
SQLiteDatabase db = getWritableDatabase();
db.execSQL("DELETE FROM events");
db.execSQL("DELETE FROM cals");
}
// --------------------------------------------------------------- events
public synchronized void replaceEvents(String cal, List<Ev> evs) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
db.execSQL("DELETE FROM events WHERE cal=?", new Object[] {cal});
for (Ev e : evs) {
db.execSQL("INSERT INTO events (cal, href, etag, uid, title, location, descr, dtstart, dtend, allday,"
+ " recurring) VALUES (?,?,?,?,?,?,?,?,?,?,?)", new Object[] {cal, e.href, e.etag, e.uid,
e.title, e.location, e.descr, Long.valueOf(e.start), Long.valueOf(e.end),
Integer.valueOf(e.allDay ? 1 : 0), Integer.valueOf(e.recurring ? 1 : 0)});
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
static final String EV_COLS = "e._id, e.cal, e.href, e.etag, e.uid, e.title, e.location, e.descr, e.dtstart,"
+ " e.dtend, e.allday, e.recurring, c.color, c.writable";
static Ev read(Cursor c) {
Ev e = new Ev();
e.id = c.getLong(0);
e.cal = c.getString(1);
e.href = c.getString(2);
e.etag = c.getString(3);
e.uid = c.getString(4);
e.title = c.getString(5);
e.location = c.getString(6);
e.descr = c.getString(7);
e.start = c.getLong(8);
e.end = c.getLong(9);
e.allDay = c.getInt(10) != 0;
e.recurring = c.getInt(11) != 0;
e.color = c.getInt(12);
e.writable = c.getInt(13) != 0;
return e;
}
List<Ev> query(String where, String[] args) {
List<Ev> out = new ArrayList<Ev>();
Cursor c = getReadableDatabase().rawQuery("SELECT " + EV_COLS + " FROM events e JOIN cals c ON c.href = e.cal"
+ " WHERE c.enabled = 1 AND (" + where + ") ORDER BY e.dtstart, e.allday DESC, e.title", args);
try {
while (c.moveToNext()) out.add(read(c));
} finally {
c.close();
}
return out;
}
/** Everything still running or starting after `from`. */
public synchronized List<Ev> agenda(long from, long to) {
return query("e.dtend > ? AND e.dtstart < ? OR (e.dtstart = e.dtend AND e.dtstart >= ? AND e.dtstart < ?)",
new String[] {String.valueOf(from), String.valueOf(to), String.valueOf(from), String.valueOf(to)});
}
public synchronized Ev event(long id) {
List<Ev> l = query("e._id = ?", new String[] {String.valueOf(id)});
return l.isEmpty() ? null : l.get(0);
}
/** Timed events starting in (after, until]. */
public synchronized List<Ev> startingBetween(long after, long until) {
return query("e.allday = 0 AND e.dtstart > ? AND e.dtstart <= ?",
new String[] {String.valueOf(after), String.valueOf(until)});
}
public synchronized long nextStartAfter(long after) {
Cursor c = getReadableDatabase().rawQuery("SELECT MIN(e.dtstart) FROM events e JOIN cals c ON c.href = e.cal"
+ " WHERE c.enabled = 1 AND e.allday = 0 AND e.dtstart > ?", new String[] {String.valueOf(after)});
try {
if (c.moveToNext() && !c.isNull(0)) return c.getLong(0);
return -1;
} finally {
c.close();
}
}
public synchronized int count() {
Cursor c = getReadableDatabase().rawQuery("SELECT COUNT(*) FROM events", null);
try {
return c.moveToNext() ? c.getInt(0) : 0;
} finally {
c.close();
}
}
}