mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-21 00:59:49 +01:00
Add avatar picker and defaults.
This commit is contained in:
committed by
Greyson Parrelli
parent
0093e1d3eb
commit
ed23c3fe7c
@@ -31,6 +31,7 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.database.helpers.ClassicOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherMigrationHelper;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase;
|
||||
import org.thoughtcrime.securesms.migrations.LegacyMigrationJob;
|
||||
import org.thoughtcrime.securesms.util.SqlUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
@@ -70,6 +71,7 @@ public class DatabaseFactory {
|
||||
private final ChatColorsDatabase chatColorsDatabase;
|
||||
private final EmojiSearchDatabase emojiSearchDatabase;
|
||||
private final MessageSendLogDatabase messageSendLogDatabase;
|
||||
private final AvatarPickerDatabase avatarPickerDatabase;
|
||||
|
||||
public static DatabaseFactory getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
@@ -200,6 +202,10 @@ public class DatabaseFactory {
|
||||
return getInstance(context).messageSendLogDatabase;
|
||||
}
|
||||
|
||||
public static AvatarPickerDatabase getAvatarPickerDatabase(Context context) {
|
||||
return getInstance(context).avatarPickerDatabase;
|
||||
}
|
||||
|
||||
public static SQLiteDatabase getBackupDatabase(Context context) {
|
||||
return getInstance(context).databaseHelper.getReadableDatabase().getSqlCipherDatabase();
|
||||
}
|
||||
@@ -259,8 +265,9 @@ public class DatabaseFactory {
|
||||
this.mentionDatabase = new MentionDatabase(context, databaseHelper);
|
||||
this.paymentDatabase = new PaymentDatabase(context, databaseHelper);
|
||||
this.chatColorsDatabase = new ChatColorsDatabase(context, databaseHelper);
|
||||
this.emojiSearchDatabase = new EmojiSearchDatabase(context, databaseHelper);
|
||||
this.messageSendLogDatabase = new MessageSendLogDatabase(context, databaseHelper);
|
||||
this.emojiSearchDatabase = new EmojiSearchDatabase(context, databaseHelper);
|
||||
this.messageSendLogDatabase = new MessageSendLogDatabase(context, databaseHelper);
|
||||
this.avatarPickerDatabase = new AvatarPickerDatabase(context, databaseHelper);
|
||||
}
|
||||
|
||||
public void onApplicationLevelUpgrade(@NonNull Context context, @NonNull MasterSecret masterSecret,
|
||||
|
||||
@@ -57,6 +57,7 @@ import org.thoughtcrime.securesms.database.SqlCipherErrorHandler;
|
||||
import org.thoughtcrime.securesms.database.StickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.database.UnknownStorageIdDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.ReactionList;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
@@ -207,8 +208,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||
private static final int THREAD_AUTOINCREMENT = 108;
|
||||
private static final int MMS_AUTOINCREMENT = 109;
|
||||
private static final int ABANDONED_ATTACHMENT_CLEANUP = 110;
|
||||
private static final int AVATAR_PICKER = 111;
|
||||
|
||||
private static final int DATABASE_VERSION = 110;
|
||||
private static final int DATABASE_VERSION = 111;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
|
||||
private final Context context;
|
||||
@@ -245,6 +247,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||
db.execSQL(PaymentDatabase.CREATE_TABLE);
|
||||
db.execSQL(ChatColorsDatabase.CREATE_TABLE);
|
||||
db.execSQL(EmojiSearchDatabase.CREATE_TABLE);
|
||||
db.execSQL(AvatarPickerDatabase.CREATE_TABLE);
|
||||
executeStatements(db, SearchDatabase.CREATE_TABLE);
|
||||
executeStatements(db, RemappedRecordsDatabase.CREATE_TABLE);
|
||||
executeStatements(db, MessageSendLogDatabase.CREATE_TABLE);
|
||||
@@ -1934,6 +1937,24 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab
|
||||
db.delete("part", "mid != -8675309 AND mid NOT IN (SELECT _id FROM mms)", null);
|
||||
}
|
||||
|
||||
if (oldVersion < AVATAR_PICKER) {
|
||||
db.execSQL("CREATE TABLE avatar_picker (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"last_used INTEGER DEFAULT 0, " +
|
||||
"group_id TEXT DEFAULT NULL, " +
|
||||
"avatar BLOB NOT NULL)");
|
||||
|
||||
try (Cursor cursor = db.query("recipient", new String[] { "_id" }, "color IS NULL", null, null, null, null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
long id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
|
||||
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put("color", AvatarColor.random().serialize());
|
||||
|
||||
db.update("recipient", values, "_id = ?", new String[] { String.valueOf(id) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package org.thoughtcrime.securesms.database.model
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import org.thoughtcrime.securesms.avatar.Avatar
|
||||
import org.thoughtcrime.securesms.avatar.Avatars
|
||||
import org.thoughtcrime.securesms.database.Database
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.database.model.databaseprotos.CustomAvatar
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.util.CursorUtil
|
||||
import org.thoughtcrime.securesms.util.SqlUtil
|
||||
|
||||
/**
|
||||
* Database which manages the record keeping for custom created avatars.
|
||||
*/
|
||||
class AvatarPickerDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Database(context, databaseHelper) {
|
||||
|
||||
companion object {
|
||||
private const val TABLE_NAME = "avatar_picker"
|
||||
private const val ID = "_id"
|
||||
private const val LAST_USED = "last_used"
|
||||
private const val GROUP_ID = "group_id"
|
||||
private const val AVATAR = "avatar"
|
||||
|
||||
//language=sql
|
||||
@JvmField
|
||||
val CREATE_TABLE = """
|
||||
CREATE TABLE $TABLE_NAME (
|
||||
$ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
$LAST_USED INTEGER DEFAULT 0,
|
||||
$GROUP_ID TEXT DEFAULT NULL,
|
||||
$AVATAR BLOB NOT NULL
|
||||
)
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
fun saveAvatarForSelf(avatar: Avatar): Avatar {
|
||||
return saveAvatar(avatar, null)
|
||||
}
|
||||
|
||||
fun saveAvatarForGroup(avatar: Avatar, groupId: GroupId): Avatar {
|
||||
return saveAvatar(avatar, groupId)
|
||||
}
|
||||
|
||||
fun markUsage(avatar: Avatar) {
|
||||
val databaseId = avatar.databaseId
|
||||
if (databaseId !is Avatar.DatabaseId.Saved) {
|
||||
throw IllegalArgumentException("Must save this avatar before trying to mark usage.")
|
||||
}
|
||||
|
||||
val db = databaseHelper.writableDatabase
|
||||
val where = ID_WHERE
|
||||
val args = SqlUtil.buildArgs(databaseId.id)
|
||||
val values = ContentValues(1)
|
||||
|
||||
values.put(LAST_USED, System.currentTimeMillis())
|
||||
db.update(TABLE_NAME, values, where, args)
|
||||
}
|
||||
|
||||
fun update(avatar: Avatar) {
|
||||
val databaseId = avatar.databaseId
|
||||
if (databaseId !is Avatar.DatabaseId.Saved) {
|
||||
throw IllegalArgumentException("Cannot update an unsaved avatar")
|
||||
}
|
||||
|
||||
val db = databaseHelper.writableDatabase
|
||||
val where = ID_WHERE
|
||||
val values = ContentValues(1)
|
||||
|
||||
values.put(AVATAR, avatar.toProto().toByteArray())
|
||||
db.update(TABLE_NAME, values, where, SqlUtil.buildArgs(databaseId.id))
|
||||
}
|
||||
|
||||
fun deleteAvatar(avatar: Avatar) {
|
||||
val databaseId = avatar.databaseId
|
||||
if (databaseId !is Avatar.DatabaseId.Saved) {
|
||||
throw IllegalArgumentException("Cannot delete an unsaved avatar.")
|
||||
}
|
||||
|
||||
val db = databaseHelper.writableDatabase
|
||||
val where = ID_WHERE
|
||||
val args = SqlUtil.buildArgs(databaseId.id)
|
||||
|
||||
db.delete(TABLE_NAME, where, args)
|
||||
}
|
||||
|
||||
private fun saveAvatar(avatar: Avatar, groupId: GroupId?): Avatar {
|
||||
val db = databaseHelper.writableDatabase
|
||||
val databaseId = avatar.databaseId
|
||||
|
||||
if (databaseId is Avatar.DatabaseId.DoNotPersist) {
|
||||
throw IllegalArgumentException("Cannot persist this avatar")
|
||||
}
|
||||
|
||||
if (databaseId is Avatar.DatabaseId.Saved) {
|
||||
val values = ContentValues(2)
|
||||
values.put(AVATAR, avatar.toProto().toByteArray())
|
||||
|
||||
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(databaseId.id))
|
||||
|
||||
return avatar
|
||||
} else {
|
||||
val values = ContentValues(4)
|
||||
values.put(AVATAR, avatar.toProto().toByteArray())
|
||||
|
||||
if (groupId != null) {
|
||||
values.put(GROUP_ID, groupId.toString())
|
||||
}
|
||||
|
||||
val id = db.insert(TABLE_NAME, null, values)
|
||||
if (id == -1L) {
|
||||
throw AssertionError("Failed to save avatar")
|
||||
}
|
||||
|
||||
return avatar.withDatabaseId(Avatar.DatabaseId.Saved(id))
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllAvatars(): List<Avatar> {
|
||||
val db = databaseHelper.readableDatabase
|
||||
val results = mutableListOf<Avatar>()
|
||||
|
||||
db.query(TABLE_NAME, SqlUtil.buildArgs(ID, AVATAR), null, null, null, null, null)?.use {
|
||||
while (it.moveToNext()) {
|
||||
val id = CursorUtil.requireLong(it, ID)
|
||||
val blob = CursorUtil.requireBlob(it, AVATAR)
|
||||
val proto = CustomAvatar.parseFrom(blob)
|
||||
results.add(proto.toAvatar(id))
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
fun getAvatarsForSelf(): List<Avatar> {
|
||||
return getAvatars(null)
|
||||
}
|
||||
|
||||
fun getAvatarsForGroup(groupId: GroupId): List<Avatar> {
|
||||
return getAvatars(groupId)
|
||||
}
|
||||
|
||||
private fun getAvatars(groupId: GroupId?): List<Avatar> {
|
||||
val db = databaseHelper.readableDatabase
|
||||
val orderBy = "$LAST_USED DESC"
|
||||
val results = mutableListOf<Avatar>()
|
||||
|
||||
val (where, args) = if (groupId == null) {
|
||||
Pair("$GROUP_ID is NULL", null)
|
||||
} else {
|
||||
Pair("$GROUP_ID = ?", SqlUtil.buildArgs(groupId))
|
||||
}
|
||||
|
||||
db.query(TABLE_NAME, SqlUtil.buildArgs(ID, AVATAR), where, args, null, null, orderBy)?.use {
|
||||
while (it.moveToNext()) {
|
||||
val id = CursorUtil.requireLong(it, ID)
|
||||
val blob = CursorUtil.requireBlob(it, AVATAR)
|
||||
val proto = CustomAvatar.parseFrom(blob)
|
||||
results.add(proto.toAvatar(id))
|
||||
}
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
private fun Avatar.toProto(): CustomAvatar {
|
||||
return when (this) {
|
||||
is Avatar.Photo -> CustomAvatar.newBuilder().setPhoto(CustomAvatar.Photo.newBuilder().setUri(this.uri.toString())).build()
|
||||
is Avatar.Text -> CustomAvatar.newBuilder().setText(CustomAvatar.Text.newBuilder().setText(this.text).setColors(this.color.code)).build()
|
||||
is Avatar.Vector -> CustomAvatar.newBuilder().setVector(CustomAvatar.Vector.newBuilder().setKey(this.key).setColors(this.color.code)).build()
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
|
||||
private fun CustomAvatar.toAvatar(id: Long): Avatar {
|
||||
return when {
|
||||
hasPhoto() -> Avatar.Photo(Uri.parse(photo.uri), photo.size, Avatar.DatabaseId.Saved(id))
|
||||
hasText() -> Avatar.Text(text.text, Avatars.colorMap[text.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
|
||||
hasVector() -> Avatar.Vector(vector.key, Avatars.colorMap[vector.colors] ?: Avatars.colors[0], Avatar.DatabaseId.Saved(id))
|
||||
else -> throw AssertionError()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user