mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-22 07:57:30 +00:00
Add support for resuming compose drafts.
This commit is contained in:
parent
209711ae40
commit
bf92de394b
@ -50,11 +50,15 @@ import org.thoughtcrime.securesms.crypto.AuthenticityCalculator;
|
|||||||
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
|
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
|
||||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
|
||||||
import org.thoughtcrime.securesms.crypto.KeyUtil;
|
import org.thoughtcrime.securesms.crypto.KeyUtil;
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.DraftDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
|
||||||
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
import org.thoughtcrime.securesms.mms.AttachmentManager;
|
||||||
import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter;
|
import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter;
|
||||||
import org.thoughtcrime.securesms.mms.MediaTooLargeException;
|
import org.thoughtcrime.securesms.mms.MediaTooLargeException;
|
||||||
|
import org.thoughtcrime.securesms.mms.Slide;
|
||||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||||
import org.thoughtcrime.securesms.protocol.Tag;
|
import org.thoughtcrime.securesms.protocol.Tag;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
@ -67,6 +71,7 @@ import org.thoughtcrime.securesms.util.CharacterCalculator;
|
|||||||
import org.thoughtcrime.securesms.util.EncryptedCharacterCalculator;
|
import org.thoughtcrime.securesms.util.EncryptedCharacterCalculator;
|
||||||
import org.thoughtcrime.securesms.util.InvalidMessageException;
|
import org.thoughtcrime.securesms.util.InvalidMessageException;
|
||||||
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
import org.thoughtcrime.securesms.util.MemoryCleaner;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
import ws.com.google.android.mms.MmsException;
|
||||||
|
|
||||||
@ -171,6 +176,7 @@ public class ConversationActivity extends SherlockFragmentActivity
|
|||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
unregisterReceiver(killActivityReceiver);
|
unregisterReceiver(killActivityReceiver);
|
||||||
unregisterReceiver(securityUpdateReceiver);
|
unregisterReceiver(securityUpdateReceiver);
|
||||||
|
saveDraft();
|
||||||
MemoryCleaner.clean(masterSecret);
|
MemoryCleaner.clean(masterSecret);
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
@ -422,6 +428,35 @@ public class ConversationActivity extends SherlockFragmentActivity
|
|||||||
if (draftText != null) composeText.setText(draftText);
|
if (draftText != null) composeText.setText(draftText);
|
||||||
if (draftImage != null) addAttachmentImage(draftImage);
|
if (draftImage != null) addAttachmentImage(draftImage);
|
||||||
if (draftAudio != null) addAttachmentAudio(draftAudio);
|
if (draftAudio != null) addAttachmentAudio(draftAudio);
|
||||||
|
|
||||||
|
if (draftText == null && draftImage == null && draftAudio == null) {
|
||||||
|
initializeDraftFromDatabase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeDraftFromDatabase() {
|
||||||
|
new AsyncTask<Void, Void, List<Draft>>() {
|
||||||
|
@Override
|
||||||
|
protected List<Draft> doInBackground(Void... params) {
|
||||||
|
MasterCipher masterCipher = new MasterCipher(masterSecret);
|
||||||
|
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
|
||||||
|
List<Draft> results = draftDatabase.getDrafts(masterCipher, threadId);
|
||||||
|
|
||||||
|
draftDatabase.clearDrafts(threadId);
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(List<Draft> drafts) {
|
||||||
|
for (Draft draft : drafts) {
|
||||||
|
if (draft.getType().equals(Draft.TEXT)) composeText.setText(draft.getValue());
|
||||||
|
else if (draft.getType().equals(Draft.IMAGE)) addAttachmentImage(Uri.parse(draft.getValue()));
|
||||||
|
else if (draft.getType().equals(Draft.AUDIO)) addAttachmentAudio(Uri.parse(draft.getValue()));
|
||||||
|
else if (draft.getType().equals(Draft.VIDEO)) addAttachmentVideo(Uri.parse(draft.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeSecurity() {
|
private void initializeSecurity() {
|
||||||
@ -571,6 +606,50 @@ public class ConversationActivity extends SherlockFragmentActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Draft> getDraftsForCurrentState() {
|
||||||
|
List<Draft> drafts = new LinkedList<Draft>();
|
||||||
|
|
||||||
|
if (!Util.isEmpty(composeText)) {
|
||||||
|
drafts.add(new Draft(Draft.TEXT, composeText.getText().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Slide slide : attachmentManager.getSlideDeck().getSlides()) {
|
||||||
|
if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
|
||||||
|
else if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
|
||||||
|
else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return drafts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveDraft() {
|
||||||
|
if (this.threadId <= 0 || this.recipients == null || this.recipients.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
final List<Draft> drafts = getDraftsForCurrentState();
|
||||||
|
|
||||||
|
if (drafts.size() <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final long thisThreadId = this.threadId;
|
||||||
|
final MasterSecret thisMasterSecret = this.masterSecret.parcelClone();
|
||||||
|
|
||||||
|
new AsyncTask<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
Toast.makeText(ConversationActivity.this, "Saving draft...", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
MasterCipher masterCipher = new MasterCipher(thisMasterSecret);
|
||||||
|
DatabaseFactory.getDraftDatabase(ConversationActivity.this).insertDrafts(masterCipher, thisThreadId, drafts);
|
||||||
|
MemoryCleaner.clean(thisMasterSecret);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
|
||||||
private void calculateCharactersRemaining() {
|
private void calculateCharactersRemaining() {
|
||||||
int charactersSpent = composeText.getText().length();
|
int charactersSpent = composeText.getText().length();
|
||||||
CharacterCalculator.CharacterState characterState = characterCalculator.calculateCharacters(charactersSpent);
|
CharacterCalculator.CharacterState characterState = characterCalculator.calculateCharacters(charactersSpent);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,89 +10,111 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.crypto;
|
package org.thoughtcrime.securesms.crypto;
|
||||||
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
|
||||||
import org.bouncycastle.util.Arrays;
|
import org.bouncycastle.util.Arrays;
|
||||||
|
|
||||||
import android.os.Parcel;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When a user first initializes TextSecure, a few secrets
|
* When a user first initializes TextSecure, a few secrets
|
||||||
* are generated. These are:
|
* are generated. These are:
|
||||||
*
|
*
|
||||||
* 1) A 128bit symmetric encryption key.
|
* 1) A 128bit symmetric encryption key.
|
||||||
* 2) A 160bit symmetric MAC key.
|
* 2) A 160bit symmetric MAC key.
|
||||||
* 3) An ECC keypair.
|
* 3) An ECC keypair.
|
||||||
*
|
*
|
||||||
* The first two, along with the ECC keypair's private key, are
|
* The first two, along with the ECC keypair's private key, are
|
||||||
* then encrypted on disk using PBE.
|
* then encrypted on disk using PBE.
|
||||||
*
|
*
|
||||||
* This class represents 1 and 2.
|
* This class represents 1 and 2.
|
||||||
*
|
*
|
||||||
* @author Moxie Marlinspike
|
* @author Moxie Marlinspike
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class MasterSecret implements Parcelable {
|
public class MasterSecret implements Parcelable {
|
||||||
|
|
||||||
private final SecretKeySpec encryptionKey;
|
private final SecretKeySpec encryptionKey;
|
||||||
private final SecretKeySpec macKey;
|
private final SecretKeySpec macKey;
|
||||||
|
|
||||||
public static final Parcelable.Creator<MasterSecret> CREATOR = new Parcelable.Creator<MasterSecret>() {
|
public static final Parcelable.Creator<MasterSecret> CREATOR = new Parcelable.Creator<MasterSecret>() {
|
||||||
|
@Override
|
||||||
public MasterSecret createFromParcel(Parcel in) {
|
public MasterSecret createFromParcel(Parcel in) {
|
||||||
return new MasterSecret(in);
|
return new MasterSecret(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public MasterSecret[] newArray(int size) {
|
public MasterSecret[] newArray(int size) {
|
||||||
return new MasterSecret[size];
|
return new MasterSecret[size];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public MasterSecret(SecretKeySpec encryptionKey, SecretKeySpec macKey) {
|
public MasterSecret(SecretKeySpec encryptionKey, SecretKeySpec macKey) {
|
||||||
this.encryptionKey = encryptionKey;
|
this.encryptionKey = encryptionKey;
|
||||||
this.macKey = macKey;
|
this.macKey = macKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MasterSecret(Parcel in) {
|
private MasterSecret(Parcel in) {
|
||||||
byte[] encryptionKeyBytes = new byte[in.readInt()];
|
byte[] encryptionKeyBytes = new byte[in.readInt()];
|
||||||
in.readByteArray(encryptionKeyBytes);
|
in.readByteArray(encryptionKeyBytes);
|
||||||
|
|
||||||
byte[] macKeyBytes = new byte[in.readInt()];
|
byte[] macKeyBytes = new byte[in.readInt()];
|
||||||
in.readByteArray(macKeyBytes);
|
in.readByteArray(macKeyBytes);
|
||||||
|
|
||||||
this.encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
this.encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
||||||
this.macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1");
|
this.macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1");
|
||||||
|
|
||||||
// SecretKeySpec does an internal copy in its constructor.
|
// SecretKeySpec does an internal copy in its constructor.
|
||||||
Arrays.fill(encryptionKeyBytes, (byte)0x00);
|
Arrays.fill(encryptionKeyBytes, (byte)0x00);
|
||||||
Arrays.fill(macKeyBytes, (byte)0x00);
|
Arrays.fill(macKeyBytes, (byte)0x00);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SecretKeySpec getEncryptionKey() {
|
public SecretKeySpec getEncryptionKey() {
|
||||||
return this.encryptionKey;
|
return this.encryptionKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SecretKeySpec getMacKey() {
|
public SecretKeySpec getMacKey() {
|
||||||
return this.macKey;
|
return this.macKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
out.writeInt(encryptionKey.getEncoded().length);
|
out.writeInt(encryptionKey.getEncoded().length);
|
||||||
out.writeByteArray(encryptionKey.getEncoded());
|
out.writeByteArray(encryptionKey.getEncoded());
|
||||||
out.writeInt(macKey.getEncoded().length);
|
out.writeInt(macKey.getEncoded().length);
|
||||||
out.writeByteArray(macKey.getEncoded());
|
out.writeByteArray(macKey.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MasterSecret parcelClone() {
|
||||||
|
Parcel thisParcel = Parcel.obtain();
|
||||||
|
Parcel thatParcel = Parcel.obtain();
|
||||||
|
byte[] bytes = null;
|
||||||
|
|
||||||
|
thisParcel.writeValue(this);
|
||||||
|
bytes = thisParcel.marshall();
|
||||||
|
|
||||||
|
thatParcel.unmarshall(bytes, 0, bytes.length);
|
||||||
|
thatParcel.setDataPosition(0);
|
||||||
|
|
||||||
|
MasterSecret that = (MasterSecret)thatParcel.readValue(MasterSecret.class.getClassLoader());
|
||||||
|
|
||||||
|
thisParcel.recycle();
|
||||||
|
thatParcel.recycle();
|
||||||
|
|
||||||
|
return that;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,8 @@ public class DatabaseFactory {
|
|||||||
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
|
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
|
||||||
private static final int INTRODUCED_INDEXES_VERSION = 3;
|
private static final int INTRODUCED_INDEXES_VERSION = 3;
|
||||||
private static final int INTRODUCED_DATE_SENT_VERSION = 4;
|
private static final int INTRODUCED_DATE_SENT_VERSION = 4;
|
||||||
private static final int DATABASE_VERSION = 4;
|
private static final int INTRODUCED_DRAFTS_VERSION = 5;
|
||||||
|
private static final int DATABASE_VERSION = 5;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "messages.db";
|
private static final String DATABASE_NAME = "messages.db";
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
@ -48,6 +49,7 @@ public class DatabaseFactory {
|
|||||||
private final MmsAddressDatabase mmsAddress;
|
private final MmsAddressDatabase mmsAddress;
|
||||||
private final MmsSmsDatabase mmsSmsDatabase;
|
private final MmsSmsDatabase mmsSmsDatabase;
|
||||||
private final IdentityDatabase identityDatabase;
|
private final IdentityDatabase identityDatabase;
|
||||||
|
private final DraftDatabase draftDatabase;
|
||||||
|
|
||||||
public static DatabaseFactory getInstance(Context context) {
|
public static DatabaseFactory getInstance(Context context) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
@ -116,6 +118,10 @@ public class DatabaseFactory {
|
|||||||
return getInstance(context).identityDatabase;
|
return getInstance(context).identityDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DraftDatabase getDraftDatabase(Context context) {
|
||||||
|
return getInstance(context).draftDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
private DatabaseFactory(Context context) {
|
private DatabaseFactory(Context context) {
|
||||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
this.sms = new SmsDatabase(context, databaseHelper);
|
this.sms = new SmsDatabase(context, databaseHelper);
|
||||||
@ -127,6 +133,7 @@ public class DatabaseFactory {
|
|||||||
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
|
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
|
||||||
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
|
||||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||||
|
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
@ -149,12 +156,14 @@ public class DatabaseFactory {
|
|||||||
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
db.execSQL(ThreadDatabase.CREATE_TABLE);
|
||||||
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
|
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
|
||||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||||
|
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||||
|
|
||||||
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, PartDatabase.CREATE_INDEXS);
|
executeStatements(db, PartDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
|
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
|
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
|
||||||
|
executeStatements(db, DraftDatabase.CREATE_INDEXS);
|
||||||
|
|
||||||
// db.execSQL(CanonicalAddress.CREATE_TABLE);
|
// db.execSQL(CanonicalAddress.CREATE_TABLE);
|
||||||
}
|
}
|
||||||
@ -187,6 +196,14 @@ public class DatabaseFactory {
|
|||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < INTRODUCED_DRAFTS_VERSION) {
|
||||||
|
db.beginTransaction();
|
||||||
|
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||||
|
executeStatements(db, DraftDatabase.CREATE_INDEXS);
|
||||||
|
db.setTransactionSuccessful();
|
||||||
|
db.endTransaction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeStatements(SQLiteDatabase db, String[] statements) {
|
private void executeStatements(SQLiteDatabase db, String[] statements) {
|
||||||
|
102
src/org/thoughtcrime/securesms/database/DraftDatabase.java
Normal file
102
src/org/thoughtcrime/securesms/database/DraftDatabase.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
|
import org.thoughtcrime.securesms.util.InvalidMessageException;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DraftDatabase extends Database {
|
||||||
|
|
||||||
|
private static final String TABLE_NAME = "drafts";
|
||||||
|
public static final String ID = "_id";
|
||||||
|
public static final String THREAD_ID = "thread_id";
|
||||||
|
public static final String DRAFT_TYPE = "type";
|
||||||
|
public static final String DRAFT_VALUE = "value";
|
||||||
|
|
||||||
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
|
THREAD_ID + " INTEGER, " + DRAFT_TYPE + " TEXT, " + DRAFT_VALUE + " TEXT);";
|
||||||
|
|
||||||
|
public static final String[] CREATE_INDEXS = {
|
||||||
|
"CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
|
||||||
|
};
|
||||||
|
|
||||||
|
public DraftDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
|
super(context, databaseHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertDrafts(MasterCipher masterCipher, long threadId, List<Draft> drafts) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
|
||||||
|
for (Draft draft : drafts) {
|
||||||
|
ContentValues values = new ContentValues(3);
|
||||||
|
values.put(THREAD_ID, threadId);
|
||||||
|
values.put(DRAFT_TYPE, masterCipher.encryptBody(draft.getType()));
|
||||||
|
values.put(DRAFT_VALUE, masterCipher.encryptBody(draft.getValue()));
|
||||||
|
|
||||||
|
db.insert(TABLE_NAME, null, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearDrafts(long threadId) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Draft> getDrafts(MasterCipher masterCipher, long threadId) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
List<Draft> results = new LinkedList<Draft>();
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
|
||||||
|
|
||||||
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
|
try {
|
||||||
|
String encryptedType = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE));
|
||||||
|
String encryptedValue = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
|
||||||
|
|
||||||
|
results.add(new Draft(masterCipher.decryptBody(encryptedType),
|
||||||
|
masterCipher.decryptBody(encryptedValue)));
|
||||||
|
} catch (InvalidMessageException ime) {
|
||||||
|
Log.w("DraftDatabase", ime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Draft {
|
||||||
|
public static final String TEXT = "text";
|
||||||
|
public static final String IMAGE = "image";
|
||||||
|
public static final String VIDEO = "video";
|
||||||
|
public static final String AUDIO = "audio";
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public Draft(String type, String value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -80,6 +82,14 @@ public class Util {
|
|||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(String value) {
|
||||||
|
return value == null || value.trim().length() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(EditText value) {
|
||||||
|
return value == null || value.getText() == null || isEmpty(value.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
// public static Bitmap loadScaledBitmap(InputStream src, int targetWidth, int targetHeight) {
|
// public static Bitmap loadScaledBitmap(InputStream src, int targetWidth, int targetHeight) {
|
||||||
// return BitmapFactory.decodeStream(src);
|
// return BitmapFactory.decodeStream(src);
|
||||||
//// BitmapFactory.Options options = new BitmapFactory.Options();
|
//// BitmapFactory.Options options = new BitmapFactory.Options();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user