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		return cursor.getInt(0);
147	}
148
149	public CopyOnWriteArrayList<Conversation> getConversations(int status) {
150		CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>();
151		SQLiteDatabase db = this.getReadableDatabase();
152		String[] selectionArgs = { Integer.toString(status) };
153		Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
154				+ " where " + Conversation.STATUS + " = ? order by "
155				+ Conversation.CREATED + " desc", selectionArgs);
156		while (cursor.moveToNext()) {
157			list.add(Conversation.fromCursor(cursor));
158		}
159		return list;
160	}
161
162	public ArrayList<Message> getMessages(Conversation conversations, int limit) {
163		return getMessages(conversations, limit, -1);
164	}
165
166	public ArrayList<Message> getMessages(Conversation conversation, int limit,
167			long timestamp) {
168		ArrayList<Message> list = new ArrayList<Message>();
169		SQLiteDatabase db = this.getReadableDatabase();
170		Cursor cursor;
171		if (timestamp == -1) {
172			String[] selectionArgs = { conversation.getUuid() };
173			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
174					+ "=?", selectionArgs, null, null, Message.TIME_SENT
175					+ " DESC", String.valueOf(limit));
176		} else {
177			String[] selectionArgs = { conversation.getUuid(),
178					Long.toString(timestamp) };
179			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
180					+ "=? and " + Message.TIME_SENT + "<?", selectionArgs,
181					null, null, Message.TIME_SENT + " DESC",
182					String.valueOf(limit));
183		}
184		if (cursor.getCount() > 0) {
185			cursor.moveToLast();
186			do {
187				Message message = Message.fromCursor(cursor);
188				message.setConversation(conversation);
189				list.add(message);
190			} while (cursor.moveToPrevious());
191		}
192		return list;
193	}
194
195	public Conversation findConversation(Account account, String contactJid) {
196		SQLiteDatabase db = this.getReadableDatabase();
197		String[] selectionArgs = { account.getUuid(), contactJid + "%" };
198		Cursor cursor = db.query(Conversation.TABLENAME, null,
199				Conversation.ACCOUNT + "=? AND " + Conversation.CONTACTJID
200						+ " like ?", selectionArgs, null, null, null);
201		if (cursor.getCount() == 0)
202			return null;
203		cursor.moveToFirst();
204		return Conversation.fromCursor(cursor);
205	}
206
207	public void updateConversation(Conversation conversation) {
208		SQLiteDatabase db = this.getWritableDatabase();
209		String[] args = { conversation.getUuid() };
210		db.update(Conversation.TABLENAME, conversation.getContentValues(),
211				Conversation.UUID + "=?", args);
212	}
213
214	public List<Account> getAccounts() {
215		List<Account> list = new ArrayList<Account>();
216		SQLiteDatabase db = this.getReadableDatabase();
217		Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
218				null, null);
219		while (cursor.moveToNext()) {
220			list.add(Account.fromCursor(cursor));
221		}
222		cursor.close();
223		return list;
224	}
225
226	public void updateAccount(Account account) {
227		SQLiteDatabase db = this.getWritableDatabase();
228		String[] args = { account.getUuid() };
229		db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
230				+ "=?", args);
231	}
232
233	public void deleteAccount(Account account) {
234		SQLiteDatabase db = this.getWritableDatabase();
235		String[] args = { account.getUuid() };
236		db.delete(Account.TABLENAME, Account.UUID + "=?", args);
237	}
238
239	public boolean hasEnabledAccounts() {
240		SQLiteDatabase db = this.getReadableDatabase();
241		Cursor cursor = db.rawQuery("select count(" + Account.UUID + ")  from "
242				+ Account.TABLENAME + " where not options & (1 <<1)", null);
243		try {
244			cursor.moveToFirst();
245			int count = cursor.getInt(0);
246			cursor.close();
247			return (count > 0);
248		} catch (SQLiteCantOpenDatabaseException e) {
249			return true; // better safe than sorry
250		}
251	}
252
253	@Override
254	public SQLiteDatabase getWritableDatabase() {
255		SQLiteDatabase db = super.getWritableDatabase();
256		db.execSQL("PRAGMA foreign_keys=ON;");
257		return db;
258	}
259
260	public void updateMessage(Message message) {
261		SQLiteDatabase db = this.getWritableDatabase();
262		String[] args = { message.getUuid() };
263		db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
264				+ "=?", args);
265	}
266
267	public void readRoster(Roster roster) {
268		SQLiteDatabase db = this.getReadableDatabase();
269		Cursor cursor;
270		String args[] = { roster.getAccount().getUuid() };
271		cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?",
272				args, null, null, null);
273		while (cursor.moveToNext()) {
274			roster.initContact(Contact.fromCursor(cursor));
275		}
276		cursor.close();
277	}
278
279	public void writeRoster(Roster roster) {
280		Account account = roster.getAccount();
281		SQLiteDatabase db = this.getWritableDatabase();
282		for (Contact contact : roster.getContacts()) {
283			if (contact.getOption(Contact.Options.IN_ROSTER)) {
284				db.insert(Contact.TABLENAME, null, contact.getContentValues());
285			} else {
286				String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
287				String[] whereArgs = { account.getUuid(), contact.getJid() };
288				db.delete(Contact.TABLENAME, where, whereArgs);
289			}
290		}
291		account.setRosterVersion(roster.getVersion());
292		updateAccount(account);
293	}
294
295	public void deleteMessage(Message message) {
296		SQLiteDatabase db = this.getWritableDatabase();
297		String[] args = { message.getUuid() };
298		db.delete(Message.TABLENAME, Message.UUID + "=?", args);
299	}
300
301	public void deleteMessagesInConversation(Conversation conversation) {
302		SQLiteDatabase db = this.getWritableDatabase();
303		String[] args = { conversation.getUuid() };
304		db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
305	}
306
307	public Conversation findConversationByUuid(String conversationUuid) {
308		SQLiteDatabase db = this.getReadableDatabase();
309		String[] selectionArgs = { conversationUuid };
310		Cursor cursor = db.query(Conversation.TABLENAME, null,
311				Conversation.UUID + "=?", selectionArgs, null, null, null);
312		if (cursor.getCount() == 0) {
313			return null;
314		}
315		cursor.moveToFirst();
316		return Conversation.fromCursor(cursor);
317	}
318
319	public Message findMessageByUuid(String messageUuid) {
320		SQLiteDatabase db = this.getReadableDatabase();
321		String[] selectionArgs = { messageUuid };
322		Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?",
323				selectionArgs, null, null, null);
324		if (cursor.getCount() == 0) {
325			return null;
326		}
327		cursor.moveToFirst();
328		return Message.fromCursor(cursor);
329	}
330
331	public Account findAccountByUuid(String accountUuid) {
332		SQLiteDatabase db = this.getReadableDatabase();
333		String[] selectionArgs = { accountUuid };
334		Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?",
335				selectionArgs, null, null, null);
336		if (cursor.getCount() == 0) {
337			return null;
338		}
339		cursor.moveToFirst();
340		return Account.fromCursor(cursor);
341	}
342
343	public List<Message> getImageMessages(Conversation conversation) {
344		ArrayList<Message> list = new ArrayList<Message>();
345		SQLiteDatabase db = this.getReadableDatabase();
346		Cursor cursor;
347			String[] selectionArgs = { conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE) };
348			cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
349					+ "=? AND "+Message.TYPE+"=?", selectionArgs, null, null,null);
350		if (cursor.getCount() > 0) {
351			cursor.moveToLast();
352			do {
353				Message message = Message.fromCursor(cursor);
354				message.setConversation(conversation);
355				list.add(message);
356			} while (cursor.moveToPrevious());
357		}
358		return list;
359	}
360}