DatabaseBackend.java

  1package eu.siacs.conversations.persistance;
  2
  3import java.util.ArrayList;
  4import java.util.List;
  5import java.util.concurrent.CopyOnWriteArrayList;
  6
  7import eu.siacs.conversations.entities.Account;
  8import eu.siacs.conversations.entities.Contact;
  9import eu.siacs.conversations.entities.Conversation;
 10import eu.siacs.conversations.entities.Message;
 11import eu.siacs.conversations.entities.Roster;
 12import android.content.Context;
 13import android.database.Cursor;
 14import android.database.sqlite.SQLiteCantOpenDatabaseException;
 15import android.database.sqlite.SQLiteDatabase;
 16import android.database.sqlite.SQLiteOpenHelper;
 17
 18public class DatabaseBackend extends SQLiteOpenHelper {
 19
 20	private static DatabaseBackend instance = null;
 21
 22	private static final String DATABASE_NAME = "history";
 23	private static final int DATABASE_VERSION = 9;
 24
 25	private static String CREATE_CONTATCS_STATEMENT = "create table "
 26			+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
 27			+ Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
 28			+ Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
 29			+ Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
 30			+ Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
 31            + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
 32			+ "FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
 33			+ Account.TABLENAME + "(" + Account.UUID
 34			+ ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
 35			+ Contact.JID + ") ON CONFLICT REPLACE);";
 36
 37	private DatabaseBackend(Context context) {
 38		super(context, DATABASE_NAME, null, DATABASE_VERSION);
 39	}
 40
 41	@Override
 42	public void onCreate(SQLiteDatabase db) {
 43		db.execSQL("PRAGMA foreign_keys=ON;");
 44		db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID
 45				+ " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
 46				+ Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
 47				+ Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
 48				+ " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
 49				+ " TEXT)");
 50		db.execSQL("create table " + Conversation.TABLENAME + " ("
 51				+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
 52				+ " TEXT, " + Conversation.CONTACT + " TEXT, "
 53				+ Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
 54				+ " TEXT, " + Conversation.CREATED + " NUMBER, "
 55				+ Conversation.STATUS + " NUMBER, " + Conversation.MODE
 56				+ " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
 57				+ Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
 58				+ "(" + Account.UUID + ") ON DELETE CASCADE);");
 59		db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
 60				+ " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
 61				+ Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
 62				+ " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
 63				+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
 64				+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
 65				+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
 66				+ Message.CONVERSATION + ") REFERENCES "
 67				+ Conversation.TABLENAME + "(" + Conversation.UUID
 68				+ ") ON DELETE CASCADE);");
 69
 70		db.execSQL(CREATE_CONTATCS_STATEMENT);
 71	}
 72
 73	@Override
 74	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 75		if (oldVersion < 2 && newVersion >= 2) {
 76			db.execSQL("update " + Account.TABLENAME + " set "
 77					+ Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
 78		}
 79		if (oldVersion < 3 && newVersion >= 3) {
 80			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
 81					+ Message.TYPE + " NUMBER");
 82		}
 83		if (oldVersion < 5 && newVersion >= 5) {
 84			db.execSQL("DROP TABLE " + Contact.TABLENAME);
 85			db.execSQL(CREATE_CONTATCS_STATEMENT);
 86			db.execSQL("UPDATE " + Account.TABLENAME + " SET "
 87					+ Account.ROSTERVERSION + " = NULL");
 88		}
 89		if (oldVersion < 6 && newVersion >= 6) {
 90			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
 91					+ Message.TRUE_COUNTERPART + " TEXT");
 92		}
 93		if (oldVersion < 7 && newVersion >= 7) {
 94			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
 95					+ Message.REMOTE_MSG_ID + " TEXT");
 96			db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
 97					+ Contact.AVATAR + " TEXT");
 98			db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
 99					+ Account.AVATAR + " TEXT");
100		}
101		if (oldVersion < 8 && newVersion >= 8) {
102			db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
103					+ Conversation.ATTRIBUTES + " TEXT");
104		}
105        if (oldVersion < 9 && newVersion >= 9) {
106            db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
107                    + Contact.LAST_TIME + " NUMBER");
108            db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
109                    + Contact.LAST_PRESENCE + " TEXT");
110        }
111	}
112
113	public static synchronized DatabaseBackend getInstance(Context context) {
114		if (instance == null) {
115			instance = new DatabaseBackend(context);
116		}
117		return instance;
118	}
119
120	public void createConversation(Conversation conversation) {
121		SQLiteDatabase db = this.getWritableDatabase();
122		db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
123	}
124
125	public void createMessage(Message message) {
126		SQLiteDatabase db = this.getWritableDatabase();
127		db.insert(Message.TABLENAME, null, message.getContentValues());
128	}
129
130	public void createAccount(Account account) {
131		SQLiteDatabase db = this.getWritableDatabase();
132		db.insert(Account.TABLENAME, null, account.getContentValues());
133	}
134
135	public void createContact(Contact contact) {
136		SQLiteDatabase db = this.getWritableDatabase();
137		db.insert(Contact.TABLENAME, null, contact.getContentValues());
138	}
139
140	public int getConversationCount() {
141		SQLiteDatabase db = this.getReadableDatabase();
142		Cursor cursor = db.rawQuery("select count(uuid) as count from "
143				+ Conversation.TABLENAME + " where " + Conversation.STATUS
144				+ "=" + Conversation.STATUS_AVAILABLE, null);
145		cursor.moveToFirst();
146		int count = cursor.getInt(0);
147		cursor.close();
148		return count;
149	}
150
151	public CopyOnWriteArrayList<Conversation> getConversations(int status) {
152		CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>();
153		SQLiteDatabase db = this.getReadableDatabase();
154		String[] selectionArgs = { Integer.toString(status) };
155		Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
156				+ " where " + Conversation.STATUS + " = ? order by "
157				+ Conversation.CREATED + " desc", selectionArgs);
158		while (cursor.moveToNext()) {
159			list.add(Conversation.fromCursor(cursor));
160		}
161		cursor.close();
162		return list;
163	}
164
165	public ArrayList<Message> getMessages(Conversation conversations, int limit) {
166		return getMessages(conversations, limit, -1);
167	}
168
169	public ArrayList<Message> getMessages(Conversation conversation, int limit,
170			long timestamp) {
171		ArrayList<Message> list = new ArrayList<Message>();
172		SQLiteDatabase db = this.getReadableDatabase();
173		Cursor cursor;
174		if (timestamp == -1) {
175			String[] selectionArgs = { conversation.getUuid() };
176			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
177					+ "=?", selectionArgs, null, null, Message.TIME_SENT
178					+ " DESC", String.valueOf(limit));
179		} else {
180			String[] selectionArgs = { conversation.getUuid(),
181					Long.toString(timestamp) };
182			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
183					+ "=? and " + Message.TIME_SENT + "<?", selectionArgs,
184					null, null, Message.TIME_SENT + " DESC",
185					String.valueOf(limit));
186		}
187		if (cursor.getCount() > 0) {
188			cursor.moveToLast();
189			do {
190				Message message = Message.fromCursor(cursor);
191				message.setConversation(conversation);
192				list.add(message);
193			} while (cursor.moveToPrevious());
194		}
195		cursor.close();
196		return list;
197	}
198
199	public Conversation findConversation(Account account, String contactJid) {
200		SQLiteDatabase db = this.getReadableDatabase();
201		String[] selectionArgs = { account.getUuid(), contactJid + "%" };
202		Cursor cursor = db.query(Conversation.TABLENAME, null,
203				Conversation.ACCOUNT + "=? AND " + Conversation.CONTACTJID
204						+ " like ?", selectionArgs, null, null, null);
205		if (cursor.getCount() == 0)
206			return null;
207		cursor.moveToFirst();
208		Conversation conversation = Conversation.fromCursor(cursor);
209		cursor.close();
210		return conversation;
211	}
212
213	public void updateConversation(Conversation conversation) {
214		SQLiteDatabase db = this.getWritableDatabase();
215		String[] args = { conversation.getUuid() };
216		db.update(Conversation.TABLENAME, conversation.getContentValues(),
217				Conversation.UUID + "=?", args);
218	}
219
220	public List<Account> getAccounts() {
221		List<Account> list = new ArrayList<Account>();
222		SQLiteDatabase db = this.getReadableDatabase();
223		Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
224				null, null);
225		while (cursor.moveToNext()) {
226			list.add(Account.fromCursor(cursor));
227		}
228		cursor.close();
229		return list;
230	}
231
232	public void updateAccount(Account account) {
233		SQLiteDatabase db = this.getWritableDatabase();
234		String[] args = { account.getUuid() };
235		db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
236				+ "=?", args);
237	}
238
239	public void deleteAccount(Account account) {
240		SQLiteDatabase db = this.getWritableDatabase();
241		String[] args = { account.getUuid() };
242		db.delete(Account.TABLENAME, Account.UUID + "=?", args);
243	}
244
245	public boolean hasEnabledAccounts() {
246		SQLiteDatabase db = this.getReadableDatabase();
247		Cursor cursor = db.rawQuery("select count(" + Account.UUID + ")  from "
248				+ Account.TABLENAME + " where not options & (1 <<1)", null);
249		try {
250			cursor.moveToFirst();
251			int count = cursor.getInt(0);
252			cursor.close();
253			return (count > 0);
254		} catch (SQLiteCantOpenDatabaseException e) {
255			return true; // better safe than sorry
256		}
257	}
258
259	@Override
260	public SQLiteDatabase getWritableDatabase() {
261		SQLiteDatabase db = super.getWritableDatabase();
262		db.execSQL("PRAGMA foreign_keys=ON;");
263		return db;
264	}
265
266	public void updateMessage(Message message) {
267		SQLiteDatabase db = this.getWritableDatabase();
268		String[] args = { message.getUuid() };
269		db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
270				+ "=?", args);
271	}
272
273	public void readRoster(Roster roster) {
274		SQLiteDatabase db = this.getReadableDatabase();
275		Cursor cursor;
276		String args[] = { roster.getAccount().getUuid() };
277		cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?",
278				args, null, null, null);
279		while (cursor.moveToNext()) {
280			roster.initContact(Contact.fromCursor(cursor));
281		}
282		cursor.close();
283	}
284
285	public void writeRoster(Roster roster) {
286		Account account = roster.getAccount();
287		SQLiteDatabase db = this.getWritableDatabase();
288		for (Contact contact : roster.getContacts()) {
289			if (contact.getOption(Contact.Options.IN_ROSTER)) {
290				db.insert(Contact.TABLENAME, null, contact.getContentValues());
291			} else {
292				String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
293				String[] whereArgs = { account.getUuid(), contact.getJid() };
294				db.delete(Contact.TABLENAME, where, whereArgs);
295			}
296		}
297		account.setRosterVersion(roster.getVersion());
298		updateAccount(account);
299	}
300
301	public void deleteMessage(Message message) {
302		SQLiteDatabase db = this.getWritableDatabase();
303		String[] args = { message.getUuid() };
304		db.delete(Message.TABLENAME, Message.UUID + "=?", args);
305	}
306
307	public void deleteMessagesInConversation(Conversation conversation) {
308		SQLiteDatabase db = this.getWritableDatabase();
309		String[] args = { conversation.getUuid() };
310		db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
311	}
312
313	public Conversation findConversationByUuid(String conversationUuid) {
314		SQLiteDatabase db = this.getReadableDatabase();
315		String[] selectionArgs = { conversationUuid };
316		Cursor cursor = db.query(Conversation.TABLENAME, null,
317				Conversation.UUID + "=?", selectionArgs, null, null, null);
318		if (cursor.getCount() == 0) {
319			return null;
320		}
321		cursor.moveToFirst();
322		Conversation conversation = Conversation.fromCursor(cursor);
323		cursor.close();
324		return conversation;
325	}
326
327	public Message findMessageByUuid(String messageUuid) {
328		SQLiteDatabase db = this.getReadableDatabase();
329		String[] selectionArgs = { messageUuid };
330		Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?",
331				selectionArgs, null, null, null);
332		if (cursor.getCount() == 0) {
333			return null;
334		}
335		cursor.moveToFirst();
336		Message message = Message.fromCursor(cursor);
337		cursor.close();
338		return message;
339	}
340
341	public Account findAccountByUuid(String accountUuid) {
342		SQLiteDatabase db = this.getReadableDatabase();
343		String[] selectionArgs = { accountUuid };
344		Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?",
345				selectionArgs, null, null, null);
346		if (cursor.getCount() == 0) {
347			return null;
348		}
349		cursor.moveToFirst();
350		Account account = Account.fromCursor(cursor);
351		cursor.close();
352		return account;
353	}
354
355	public List<Message> getImageMessages(Conversation conversation) {
356		ArrayList<Message> list = new ArrayList<Message>();
357		SQLiteDatabase db = this.getReadableDatabase();
358		Cursor cursor;
359			String[] selectionArgs = { conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE) };
360			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
361					+ "=? AND "+Message.TYPE+"=?", selectionArgs, null, null,null);
362		if (cursor.getCount() > 0) {
363			cursor.moveToLast();
364			do {
365				Message message = Message.fromCursor(cursor);
366				message.setConversation(conversation);
367				list.add(message);
368			} while (cursor.moveToPrevious());
369		}
370		cursor.close();
371		return list;
372	}
373}