From 465c852e8bb67e83831ccc57be75fd7dbc27efb9 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 7 Jan 2025 16:59:45 -0500 Subject: [PATCH] Convert StickerTable to kotlin. --- .../securesms/backup/FullBackupExporter.java | 2 +- .../securesms/backup/FullBackupImporter.java | 2 +- .../v2/processor/StickerArchiveProcessor.kt | 6 +- .../ConversationStickerSuggestionAdapter.java | 2 +- .../securesms/database/AttachmentTable.kt | 2 +- .../securesms/database/StickerTable.java | 518 ------------------ .../securesms/database/StickerTable.kt | 474 ++++++++++++++++ .../database/model/IncomingSticker.java | 74 --- .../database/model/IncomingSticker.kt | 13 + .../database/model/StickerPackRecord.java | 80 --- .../database/model/StickerPackRecord.kt | 21 + .../database/model/StickerRecord.java | 103 ---- .../securesms/database/model/StickerRecord.kt | 21 + .../jobs/MultiDeviceStickerPackSyncJob.java | 4 +- .../securesms/jobs/PushSendJob.java | 2 +- .../securesms/jobs/StickerDownloadJob.java | 2 +- .../sticker/StickerKeyboardRepository.kt | 17 +- .../sticker/StickerSearchRepository.kt | 4 +- .../ImageEditorStickerSelectActivity.java | 4 +- .../stickers/StickerManagementAdapter.java | 20 +- .../stickers/StickerManagementRepository.java | 4 +- .../StickerPackPreviewRepository.java | 16 +- .../StickerRolloverTouchListener.java | 2 +- 23 files changed, 574 insertions(+), 819 deletions(-) delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt delete mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index 5b5f879ccd..e9d1f74606 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -483,7 +483,7 @@ public class FullBackupExporter extends FullBackupBase { long estimatedCount) throws IOException { - long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerTable._ID)); + long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(StickerTable.ID)); long size = cursor.getLong(cursor.getColumnIndexOrThrow(StickerTable.FILE_LENGTH)); String data = cursor.getString(cursor.getColumnIndexOrThrow(StickerTable.FILE_PATH)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index df8543b379..d16b28d335 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -239,7 +239,7 @@ public class FullBackupImporter extends FullBackupBase { contentValues.put(StickerTable.FILE_RANDOM, output.first); db.update(StickerTable.TABLE_NAME, contentValues, - StickerTable._ID + " = ?", + StickerTable.ID + " = ?", new String[] {String.valueOf(sticker.rowId)}); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt index cf835f234c..8151a0a363 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/StickerArchiveProcessor.kt @@ -24,14 +24,14 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob */ object StickerArchiveProcessor { fun export(db: SignalDatabase, emitter: BackupFrameEmitter) { - StickerPackRecordReader(db.stickerTable.allStickerPacks).use { reader -> - var record: StickerPackRecord? = reader.next + StickerPackRecordReader(db.stickerTable.getAllStickerPacks()).use { reader -> + var record: StickerPackRecord? = reader.getNext() while (record != null) { if (record.isInstalled) { val frame = record.toBackupFrame() emitter.emit(frame) } - record = reader.next + record = reader.getNext() } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java index daffed9555..c6542465f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationStickerSuggestionAdapter.java @@ -66,7 +66,7 @@ public class ConversationStickerSuggestionAdapter extends RecyclerView.Adapter = filesOnDisk - filesInDb diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.java b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.java deleted file mode 100644 index b386de7e5b..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.java +++ /dev/null @@ -1,518 +0,0 @@ -package org.thoughtcrime.securesms.database; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Pair; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.greenrobot.eventbus.EventBus; -import org.signal.core.util.StreamUtil; -import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.crypto.AttachmentSecret; -import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; -import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; -import org.thoughtcrime.securesms.database.model.IncomingSticker; -import org.thoughtcrime.securesms.database.model.StickerPackRecord; -import org.thoughtcrime.securesms.database.model.StickerRecord; -import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri; -import org.thoughtcrime.securesms.stickers.BlessedPacks; -import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent; -import org.signal.core.util.CursorUtil; -import org.signal.core.util.SqlUtil; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public class StickerTable extends DatabaseTable { - - private static final String TAG = Log.tag(StickerTable.class); - - public static final String TABLE_NAME = "sticker"; - public static final String _ID = "_id"; - public static final String PACK_ID = "pack_id"; - public static final String PACK_KEY = "pack_key"; - public static final String PACK_TITLE = "pack_title"; - public static final String PACK_AUTHOR = "pack_author"; - private static final String STICKER_ID = "sticker_id"; - public static final String EMOJI = "emoji"; - public static final String CONTENT_TYPE = "content_type"; - public static final String COVER = "cover"; - private static final String PACK_ORDER = "pack_order"; - public static final String INSTALLED = "installed"; - private static final String LAST_USED = "last_used"; - public static final String FILE_PATH = "file_path"; - public static final String FILE_LENGTH = "file_length"; - public static final String FILE_RANDOM = "file_random"; - - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + - PACK_ID + " TEXT NOT NULL, " + - PACK_KEY + " TEXT NOT NULL, " + - PACK_TITLE + " TEXT NOT NULL, " + - PACK_AUTHOR + " TEXT NOT NULL, " + - STICKER_ID + " INTEGER, " + - COVER + " INTEGER, " + - PACK_ORDER + " INTEGER, " + - EMOJI + " TEXT NOT NULL, " + - CONTENT_TYPE + " TEXT DEFAULT NULL, " + - LAST_USED + " INTEGER, " + - INSTALLED + " INTEGER," + - FILE_PATH + " TEXT NOT NULL, " + - FILE_LENGTH + " INTEGER, " + - FILE_RANDOM + " BLOB, " + - "UNIQUE(" + PACK_ID + ", " + STICKER_ID + ", " + COVER + ") ON CONFLICT IGNORE)"; - - public static final String[] CREATE_INDEXES = { - "CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON " + TABLE_NAME + " (" + PACK_ID + ");", - "CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON " + TABLE_NAME + " (" + STICKER_ID + ");" - }; - - public static final String DIRECTORY = "stickers"; - - private final AttachmentSecret attachmentSecret; - - public StickerTable(Context context, SignalDatabase databaseHelper, AttachmentSecret attachmentSecret) { - super(context, databaseHelper); - this.attachmentSecret = attachmentSecret; - } - - public void insertSticker(@NonNull IncomingSticker sticker, @NonNull InputStream dataStream, boolean notify) throws IOException { - FileInfo fileInfo = saveStickerImage(dataStream); - ContentValues contentValues = new ContentValues(); - - contentValues.put(PACK_ID, sticker.getPackId()); - contentValues.put(PACK_KEY, sticker.getPackKey()); - contentValues.put(PACK_TITLE, sticker.getPackTitle()); - contentValues.put(PACK_AUTHOR, sticker.getPackAuthor()); - contentValues.put(STICKER_ID, sticker.getStickerId()); - contentValues.put(EMOJI, sticker.getEmoji()); - contentValues.put(CONTENT_TYPE, sticker.getContentType()); - contentValues.put(COVER, sticker.isCover() ? 1 : 0); - contentValues.put(INSTALLED, sticker.isInstalled() ? 1 : 0); - contentValues.put(FILE_PATH, fileInfo.getFile().getAbsolutePath()); - contentValues.put(FILE_LENGTH, fileInfo.getLength()); - contentValues.put(FILE_RANDOM, fileInfo.getRandom()); - - long id = databaseHelper.getSignalWritableDatabase().insert(TABLE_NAME, null, contentValues); - if (id == -1) { - String selection = PACK_ID + " = ? AND " + STICKER_ID + " = ? AND " + COVER + " = ?"; - String[] args = SqlUtil.buildArgs(sticker.getPackId(), sticker.getStickerId(), (sticker.isCover() ? 1 : 0)); - - id = databaseHelper.getSignalWritableDatabase().update(TABLE_NAME, contentValues, selection, args); - } - - if (id > 0) { - notifyStickerListeners(); - - if (sticker.isCover()) { - notifyStickerPackListeners(); - - if (sticker.isInstalled() && notify) { - broadcastInstallEvent(sticker.getPackId()); - } - } - } - } - - public @Nullable StickerRecord getSticker(@NonNull String packId, int stickerId, boolean isCover) { - String selection = PACK_ID + " = ? AND " + STICKER_ID + " = ? AND " + COVER + " = ?"; - String[] args = new String[] { packId, String.valueOf(stickerId), String.valueOf(isCover ? 1 : 0) }; - - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, "1")) { - return new StickerRecordReader(cursor).getNext(); - } - } - - public @Nullable StickerPackRecord getStickerPack(@NonNull String packId) { - String query = PACK_ID + " = ? AND " + COVER + " = ?"; - String[] args = new String[] { packId, "1" }; - - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, query, args, null, null, null, "1")) { - return new StickerPackRecordReader(cursor).getNext(); - } - } - - public @Nullable Cursor getInstalledStickerPacks() { - String selection = COVER + " = ? AND " + INSTALLED + " = ?"; - String[] args = new String[] { "1", "1" }; - - return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, PACK_ORDER + " ASC"); - } - - public @Nullable Cursor getStickersByEmoji(@NonNull String emoji) { - String selection = EMOJI + " LIKE ? AND " + COVER + " = ?"; - String[] args = new String[] { "%"+emoji+"%", "0" }; - - return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null); - } - - public @Nullable Cursor getAllStickerPacks() { - return getAllStickerPacks(null); - } - - public @Nullable Cursor getAllStickerPacks(@Nullable String limit) { - String query = COVER + " = ?"; - String[] args = new String[] { "1" }; - - return databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, query, args, null, null, PACK_ORDER + " ASC", limit); - } - - public @Nullable Cursor getStickersForPack(@NonNull String packId) { - SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); - String selection = PACK_ID + " = ? AND " + COVER + " = ?"; - String[] args = new String[] { packId, "0" }; - - return db.query(TABLE_NAME, null, selection, args, null, null, STICKER_ID + " ASC"); - } - - public @Nullable Cursor getRecentlyUsedStickers(int limit) { - SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); - String selection = LAST_USED + " > ? AND " + COVER + " = ?"; - String[] args = new String[] { "0", "0" }; - - return db.query(TABLE_NAME, null, selection, args, null, null, LAST_USED + " DESC", String.valueOf(limit)); - } - - public @NonNull Set getAllStickerFiles() { - SQLiteDatabase db = databaseHelper.getSignalReadableDatabase(); - - Set files = new HashSet<>(); - try (Cursor cursor = db.query(TABLE_NAME, new String[] { FILE_PATH }, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - files.add(CursorUtil.requireString(cursor, FILE_PATH)); - } - } - - return files; - } - - public @Nullable InputStream getStickerStream(long rowId) throws IOException { - String selection = _ID + " = ?"; - String[] args = new String[] { String.valueOf(rowId) }; - - try (Cursor cursor = databaseHelper.getSignalReadableDatabase().query(TABLE_NAME, null, selection, args, null, null, null)) { - if (cursor != null && cursor.moveToNext()) { - String path = cursor.getString(cursor.getColumnIndexOrThrow(FILE_PATH)); - byte[] random = cursor.getBlob(cursor.getColumnIndexOrThrow(FILE_RANDOM)); - - if (path != null) { - return ModernDecryptingPartInputStream.createFor(attachmentSecret, random, new File(path), 0); - } else { - Log.w(TAG, "getStickerStream("+rowId+") - No sticker data"); - } - } else { - Log.i(TAG, "getStickerStream("+rowId+") - Sticker not found."); - } - } - - return null; - } - - public boolean isPackInstalled(@NonNull String packId) { - StickerPackRecord record = getStickerPack(packId); - - return (record != null && record.isInstalled()); - } - - public boolean isPackAvailableAsReference(@NonNull String packId) { - return getStickerPack(packId) != null; - } - - public void updateStickerLastUsedTime(long rowId, long lastUsed) { - String selection = _ID + " = ?"; - String[] args = new String[] { String.valueOf(rowId) }; - ContentValues values = new ContentValues(); - - values.put(LAST_USED, lastUsed); - - databaseHelper.getSignalWritableDatabase().update(TABLE_NAME, values, selection, args); - - notifyStickerListeners(); - notifyStickerPackListeners(); - } - - public void markPackAsInstalled(@NonNull String packKey, boolean notify) { - updatePackInstalled(databaseHelper.getSignalWritableDatabase(), packKey, true, notify); - notifyStickerPackListeners(); - } - - public void deleteOrphanedPacks() { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - String query = "SELECT " + PACK_ID + " FROM " + TABLE_NAME + " WHERE " + INSTALLED + " = ? AND " + - PACK_ID + " NOT IN (" + - "SELECT DISTINCT " + AttachmentTable.STICKER_PACK_ID + " FROM " + AttachmentTable.TABLE_NAME + " " + - "WHERE " + AttachmentTable.STICKER_PACK_ID + " NOT NULL" + - ")"; - String[] args = new String[] { "0" }; - - db.beginTransaction(); - - try { - boolean performedDelete = false; - - try (Cursor cursor = db.rawQuery(query, args)) { - while (cursor != null && cursor.moveToNext()) { - String packId = cursor.getString(cursor.getColumnIndexOrThrow(PACK_ID)); - - if (!BlessedPacks.contains(packId)) { - deletePack(db, packId); - performedDelete = true; - } - } - } - - db.setTransactionSuccessful(); - - if (performedDelete) { - notifyStickerPackListeners(); - notifyStickerListeners(); - } - } finally { - db.endTransaction(); - } - } - - public void uninstallPack(@NonNull String packId) { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - updatePackInstalled(db, packId, false, false); - deleteStickersInPackExceptCover(db, packId); - - db.setTransactionSuccessful(); - notifyStickerPackListeners(); - notifyStickerListeners(); - } finally { - db.endTransaction(); - } - } - - public void updatePackOrder(@NonNull List packsInOrder) { - SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - - db.beginTransaction(); - try { - String selection = PACK_ID + " = ? AND " + COVER + " = ?"; - - for (int i = 0; i < packsInOrder.size(); i++) { - String[] args = new String[]{ packsInOrder.get(i).getPackId(), "1" }; - ContentValues values = new ContentValues(); - - values.put(PACK_ORDER, i); - - db.update(TABLE_NAME, values, selection, args); - } - - db.setTransactionSuccessful(); - notifyStickerPackListeners(); - } finally { - db.endTransaction(); - } - } - - private void updatePackInstalled(@NonNull SQLiteDatabase db, @NonNull String packId, boolean installed, boolean notify) { - StickerPackRecord existing = getStickerPack(packId); - - if (existing != null && existing.isInstalled() == installed) { - return; - } - - String selection = PACK_ID + " = ?"; - String[] args = new String[]{ packId }; - ContentValues values = new ContentValues(1); - - values.put(INSTALLED, installed ? 1 : 0); - db.update(TABLE_NAME, values, selection, args); - - if (installed && notify) { - broadcastInstallEvent(packId); - } - } - - private FileInfo saveStickerImage(@NonNull InputStream inputStream) throws IOException { - File partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE); - File file = File.createTempFile("sticker", ".mms", partsDirectory); - Pair out = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, false); - long length = StreamUtil.copy(inputStream, out.second); - - return new FileInfo(file, length, out.first); - } - - private void deleteSticker(@NonNull SQLiteDatabase db, long rowId, @Nullable String filePath) { - String selection = _ID + " = ?"; - String[] args = new String[] { String.valueOf(rowId) }; - - db.delete(TABLE_NAME, selection, args); - - if (!TextUtils.isEmpty(filePath)) { - new File(filePath).delete(); - } - } - - private void deletePack(@NonNull SQLiteDatabase db, @NonNull String packId) { - String selection = PACK_ID + " = ?"; - String[] args = new String[] { packId }; - - db.delete(TABLE_NAME, selection, args); - - deleteStickersInPack(db, packId); - } - - private void deleteStickersInPack(@NonNull SQLiteDatabase db, @NonNull String packId) { - String selection = PACK_ID + " = ?"; - String[] args = new String[] { packId }; - - db.beginTransaction(); - - try { - try (Cursor cursor = db.query(TABLE_NAME, null, selection, args, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - String filePath = cursor.getString(cursor.getColumnIndexOrThrow(FILE_PATH)); - long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(_ID)); - - deleteSticker(db, rowId, filePath); - } - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - - db.delete(TABLE_NAME, selection, args); - } - - private void deleteStickersInPackExceptCover(@NonNull SQLiteDatabase db, @NonNull String packId) { - String selection = PACK_ID + " = ? AND " + COVER + " = ?"; - String[] args = new String[] { packId, "0" }; - - db.beginTransaction(); - - try { - try (Cursor cursor = db.query(TABLE_NAME, null, selection, args, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { - long rowId = cursor.getLong(cursor.getColumnIndexOrThrow(_ID)); - String filePath = cursor.getString(cursor.getColumnIndexOrThrow(FILE_PATH)); - - deleteSticker(db, rowId, filePath); - } - } - - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - - private void broadcastInstallEvent(@NonNull String packId) { - StickerPackRecord pack = getStickerPack(packId); - - if (pack != null) { - EventBus.getDefault().postSticky(new StickerPackInstallEvent(new DecryptableUri(pack.getCover().getUri()))); - } - } - - private static final class FileInfo { - private final File file; - private final long length; - private final byte[] random; - - private FileInfo(@NonNull File file, long length, @NonNull byte[] random) { - this.file = file; - this.length = length; - this.random = random; - } - - public File getFile() { - return file; - } - - public long getLength() { - return length; - } - - public byte[] getRandom() { - return random; - } - } - - public static final class StickerRecordReader implements Closeable { - - private final Cursor cursor; - - public StickerRecordReader(@Nullable Cursor cursor) { - this.cursor = cursor; - } - - public @Nullable StickerRecord getNext() { - if (cursor == null || !cursor.moveToNext()) { - return null; - } - - return getCurrent(); - } - - public @NonNull StickerRecord getCurrent() { - return new StickerRecord(cursor.getLong(cursor.getColumnIndexOrThrow(_ID)), - cursor.getString(cursor.getColumnIndexOrThrow(PACK_ID)), - cursor.getString(cursor.getColumnIndexOrThrow(PACK_KEY)), - cursor.getInt(cursor.getColumnIndexOrThrow(STICKER_ID)), - cursor.getString(cursor.getColumnIndexOrThrow(EMOJI)), - cursor.getString(cursor.getColumnIndexOrThrow(CONTENT_TYPE)), - cursor.getLong(cursor.getColumnIndexOrThrow(FILE_LENGTH)), - cursor.getInt(cursor.getColumnIndexOrThrow(COVER)) == 1); - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } - } - - public static final class StickerPackRecordReader implements Closeable { - - private final Cursor cursor; - - public StickerPackRecordReader(@Nullable Cursor cursor) { - this.cursor = cursor; - } - - public @Nullable StickerPackRecord getNext() { - if (cursor == null || !cursor.moveToNext()) { - return null; - } - - return getCurrent(); - } - - public @NonNull StickerPackRecord getCurrent() { - StickerRecord cover = new StickerRecordReader(cursor).getCurrent(); - - return new StickerPackRecord(cursor.getString(cursor.getColumnIndexOrThrow(PACK_ID)), - cursor.getString(cursor.getColumnIndexOrThrow(PACK_KEY)), - cursor.getString(cursor.getColumnIndexOrThrow(PACK_TITLE)), - cursor.getString(cursor.getColumnIndexOrThrow(PACK_AUTHOR)), - cover, - cursor.getInt(cursor.getColumnIndexOrThrow(INSTALLED)) == 1); - } - - @Override - public void close() { - if (cursor != null) { - cursor.close(); - } - } - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt new file mode 100644 index 0000000000..f5f3d5f322 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/StickerTable.kt @@ -0,0 +1,474 @@ +package org.thoughtcrime.securesms.database + +import android.content.Context +import android.database.Cursor +import org.greenrobot.eventbus.EventBus +import org.signal.core.util.StreamUtil +import org.signal.core.util.delete +import org.signal.core.util.exists +import org.signal.core.util.forEach +import org.signal.core.util.insertInto +import org.signal.core.util.isNotNullOrBlank +import org.signal.core.util.logging.Log +import org.signal.core.util.logging.Log.tag +import org.signal.core.util.readToSet +import org.signal.core.util.readToSingleObject +import org.signal.core.util.requireBlob +import org.signal.core.util.requireBoolean +import org.signal.core.util.requireInt +import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullString +import org.signal.core.util.requireString +import org.signal.core.util.select +import org.signal.core.util.toInt +import org.signal.core.util.update +import org.signal.core.util.withinTransaction +import org.thoughtcrime.securesms.crypto.AttachmentSecret +import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream +import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream +import org.thoughtcrime.securesms.database.model.IncomingSticker +import org.thoughtcrime.securesms.database.model.StickerPackRecord +import org.thoughtcrime.securesms.database.model.StickerRecord +import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri +import org.thoughtcrime.securesms.stickers.BlessedPacks +import org.thoughtcrime.securesms.stickers.StickerPackInstallEvent +import org.thoughtcrime.securesms.util.MediaUtil +import java.io.Closeable +import java.io.File +import java.io.IOException +import java.io.InputStream + +class StickerTable( + context: Context?, + databaseHelper: SignalDatabase?, + private val attachmentSecret: AttachmentSecret +) : DatabaseTable(context, databaseHelper) { + + companion object { + private val TAG = tag(StickerTable::class.java) + + const val TABLE_NAME: String = "sticker" + const val ID: String = "_id" + const val PACK_ID: String = "pack_id" + const val PACK_KEY: String = "pack_key" + const val PACK_TITLE: String = "pack_title" + const val PACK_AUTHOR: String = "pack_author" + private const val STICKER_ID = "sticker_id" + const val EMOJI: String = "emoji" + const val CONTENT_TYPE: String = "content_type" + const val COVER: String = "cover" + private const val PACK_ORDER = "pack_order" + const val INSTALLED: String = "installed" + private const val LAST_USED = "last_used" + const val FILE_PATH: String = "file_path" + const val FILE_LENGTH: String = "file_length" + const val FILE_RANDOM: String = "file_random" + + val CREATE_TABLE: String = """ + CREATE TABLE $TABLE_NAME ( + $ID INTEGER PRIMARY KEY AUTOINCREMENT, + $PACK_ID TEXT NOT NULL, + $PACK_KEY TEXT NOT NULL, + $PACK_TITLE TEXT NOT NULL, + $PACK_AUTHOR TEXT NOT NULL, + $STICKER_ID INTEGER, + $COVER INTEGER, + $PACK_ORDER INTEGER, + $EMOJI TEXT NOT NULL, + $CONTENT_TYPE TEXT DEFAULT NULL, + $LAST_USED INTEGER, + $INSTALLED INTEGER, + $FILE_PATH TEXT NOT NULL, + $FILE_LENGTH INTEGER, + $FILE_RANDOM BLOB, + UNIQUE($PACK_ID, $STICKER_ID, $COVER) ON CONFLICT IGNORE + ) + """ + + val CREATE_INDEXES: Array = arrayOf( + "CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON $TABLE_NAME ($PACK_ID);", + "CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON $TABLE_NAME ($STICKER_ID);" + ) + + const val DIRECTORY: String = "stickers" + } + + @Throws(IOException::class) + fun insertSticker(sticker: IncomingSticker, dataStream: InputStream, notify: Boolean) { + val fileInfo: FileInfo = saveStickerImage(dataStream) + + writableDatabase + .insertInto(TABLE_NAME) + .values( + PACK_ID to sticker.packId, + PACK_KEY to sticker.packKey, + PACK_TITLE to sticker.packTitle, + PACK_AUTHOR to sticker.packAuthor, + STICKER_ID to sticker.stickerId, + EMOJI to sticker.emoji, + CONTENT_TYPE to sticker.contentType, + COVER to if (sticker.isCover) 1 else 0, + INSTALLED to if (sticker.isInstalled) 1 else 0, + FILE_PATH to fileInfo.file.absolutePath, + FILE_LENGTH to fileInfo.length, + FILE_RANDOM to fileInfo.random + ) + .run(SQLiteDatabase.CONFLICT_REPLACE) + + notifyStickerListeners() + + if (sticker.isCover) { + notifyStickerPackListeners() + + if (sticker.isInstalled && notify) { + broadcastInstallEvent(sticker.packId) + } + } + } + + fun getSticker(packId: String, stickerId: Int, isCover: Boolean): StickerRecord? { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$PACK_ID = ? AND $STICKER_ID = ? AND $COVER = ?", packId, stickerId.toString(), isCover.toInt()) + .run() + .readToSingleObject { it.readStickerRecord() } + } + + fun getStickerPack(packId: String): StickerPackRecord? { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$PACK_ID = ? AND $COVER = 1", packId) + .run() + .readToSingleObject { it.readStickerPackRecord() } + } + + fun getInstalledStickerPacks(): Cursor { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$COVER = 1 AND $INSTALLED = 1") + .orderBy("$PACK_ORDER ASC") + .run() + } + + fun getStickersByEmoji(emoji: String): Cursor { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$EMOJI LIKE ? AND $COVER = 0", "%$emoji%") + .run() + } + + fun getAllStickerPacks(): Cursor { + return getAllStickerPacks(null) + } + + fun getAllStickerPacks(limit: String?): Cursor { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$COVER = 1") + .orderBy("$PACK_ORDER ASC") + .limit(limit ?: "") + .run() + } + + fun getStickersForPack(packId: String): Cursor { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$PACK_ID = ? AND $COVER = 0", packId) + .orderBy("$STICKER_ID ASC") + .run() + } + + fun getRecentlyUsedStickers(limit: Int): Cursor { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$LAST_USED > 0 AND $COVER = 0") + .orderBy("$LAST_USED DESC") + .limit(limit) + .run() + } + + fun getAllStickerFiles(): Set { + return readableDatabase + .select(FILE_PATH) + .from(TABLE_NAME) + .run() + .readToSet { it.requireNonNullString(FILE_PATH) } + } + + @Throws(IOException::class) + fun getStickerStream(rowId: Long): InputStream? { + return readableDatabase + .select() + .from(TABLE_NAME) + .where("$ID = ?", rowId) + .run() + .use { cursor -> + if (cursor.moveToFirst()) { + val path = cursor.requireString(FILE_PATH) + val random = cursor.requireBlob(FILE_RANDOM) + + if (path != null && random != null) { + ModernDecryptingPartInputStream.createFor(attachmentSecret, random, File(path), 0) + } else { + Log.w(TAG, "getStickerStream($rowId) - No sticker data") + null + } + } else { + Log.w(TAG, "getStickerStream($rowId) - Sticker not found") + null + } + } + } + + fun isPackInstalled(packId: String): Boolean { + return getStickerPack(packId)?.isInstalled ?: false + } + + fun isPackAvailableAsReference(packId: String): Boolean { + return readableDatabase + .exists(TABLE_NAME) + .where("$PACK_ID = ? AND $COVER = 1", packId) + .run() + } + + fun updateStickerLastUsedTime(rowId: Long, lastUsed: Long) { + writableDatabase + .update(TABLE_NAME) + .values(LAST_USED to lastUsed) + .where("$ID = ?", rowId) + .run() + + notifyStickerListeners() + notifyStickerPackListeners() + } + + fun markPackAsInstalled(packKey: String, notify: Boolean) { + updatePackInstalled( + db = databaseHelper.signalWritableDatabase, + packId = packKey, + installed = true, + notify = notify + ) + notifyStickerPackListeners() + } + + fun deleteOrphanedPacks() { + var performedDelete = false + + writableDatabase.withinTransaction { db -> + db.rawQuery( + """ + SELECT $PACK_ID + FROM $TABLE_NAME + WHERE + $INSTALLED = 0 AND + $PACK_ID NOT IN ( + SELECT DISTINCT ${AttachmentTable.STICKER_PACK_ID} + FROM ${AttachmentTable.TABLE_NAME} + WHERE ${AttachmentTable.STICKER_PACK_ID} NOT NULL + ) + """, + null + ).forEach { cursor -> + val packId = cursor.getString(cursor.getColumnIndexOrThrow(PACK_ID)) + + if (!BlessedPacks.contains(packId)) { + deletePack(db, packId) + performedDelete = true + } + } + } + + if (performedDelete) { + notifyStickerPackListeners() + notifyStickerListeners() + } + } + + fun uninstallPack(packId: String) { + writableDatabase.withinTransaction { db -> + updatePackInstalled(db = db, packId = packId, installed = false, notify = false) + deleteStickersInPackExceptCover(db, packId) + } + + notifyStickerPackListeners() + notifyStickerListeners() + } + + fun updatePackOrder(packsInOrder: MutableList) { + writableDatabase.withinTransaction { db -> + for ((i, pack) in packsInOrder.withIndex()) { + db.update(TABLE_NAME) + .values(PACK_ORDER to i) + .where("$PACK_ID = ? AND $COVER = 1", pack.packId) + .run() + } + } + + notifyStickerPackListeners() + } + + private fun updatePackInstalled(db: SQLiteDatabase, packId: String, installed: Boolean, notify: Boolean) { + val existing = getStickerPack(packId) + + if (existing != null && existing.isInstalled == installed) { + return + } + + db.update(TABLE_NAME) + .values(INSTALLED to installed.toInt()) + .where("$PACK_ID = ?", packId) + .run() + + if (installed && notify) { + broadcastInstallEvent(packId) + } + } + + @Throws(IOException::class) + private fun saveStickerImage(inputStream: InputStream): FileInfo { + val partsDirectory = context.getDir(DIRECTORY, Context.MODE_PRIVATE) + val file = File.createTempFile("sticker", ".mms", partsDirectory) + val out = ModernEncryptingPartOutputStream.createFor(attachmentSecret, file, false) + val length = StreamUtil.copy(inputStream, out.second) + + return FileInfo(file, length, out.first!!) + } + + private fun deleteSticker(db: SQLiteDatabase, rowId: Long, filePath: String?) { + db.delete(TABLE_NAME) + .where("$ID = ?", rowId) + .run() + + if (filePath.isNotNullOrBlank()) { + File(filePath).delete() + } + } + + private fun deletePack(db: SQLiteDatabase, packId: String) { + db.delete(TABLE_NAME) + .where("$PACK_ID = ?", packId) + .run() + + deleteStickersInPack(db, packId) + } + + private fun deleteStickersInPack(database: SQLiteDatabase, packId: String) { + database.withinTransaction { db -> + db.select(ID, FILE_PATH) + .from(TABLE_NAME) + .where("$PACK_ID = ?", packId) + .run() + .forEach { cursor -> + val rowId = cursor.requireLong(ID) + val filePath = cursor.requireString(FILE_PATH) + + deleteSticker(db, rowId, filePath) + } + + db.delete(TABLE_NAME) + .where("$PACK_ID = ?", packId) + .run() + } + } + + private fun deleteStickersInPackExceptCover(database: SQLiteDatabase, packId: String) { + database.withinTransaction { db -> + db.select(ID, FILE_PATH) + .from(TABLE_NAME) + .where("$PACK_ID = ? AND $COVER = 0", packId) + .run() + .forEach { cursor -> + val rowId = cursor.requireLong(ID) + val filePath = cursor.requireString(FILE_PATH) + + deleteSticker(db, rowId, filePath) + } + } + } + + private fun broadcastInstallEvent(packId: String) { + val pack = getStickerPack(packId) + + if (pack != null) { + EventBus.getDefault().postSticky(StickerPackInstallEvent(DecryptableUri(pack.cover.uri))) + } + } + + private fun Cursor.readStickerRecord(): StickerRecord { + return StickerRecordReader(this).getCurrent() + } + + private fun Cursor.readStickerPackRecord(): StickerPackRecord { + return StickerPackRecordReader(this).getCurrent() + } + + private class FileInfo( + val file: File, + val length: Long, + val random: ByteArray + ) + + class StickerRecordReader(private val cursor: Cursor) : Closeable { + + fun getNext(): StickerRecord? { + if (!cursor.moveToNext()) { + return null + } + + return getCurrent() + } + + fun getCurrent(): StickerRecord { + return StickerRecord( + rowId = cursor.requireLong(ID), + packId = cursor.requireNonNullString(PACK_ID), + packKey = cursor.requireNonNullString(PACK_KEY), + stickerId = cursor.requireInt(STICKER_ID), + emoji = cursor.requireNonNullString(EMOJI), + contentType = cursor.requireString(CONTENT_TYPE) ?: MediaUtil.IMAGE_WEBP, + size = cursor.requireLong(FILE_LENGTH), + isCover = cursor.requireBoolean(COVER) + ) + } + + override fun close() { + cursor.close() + } + } + + class StickerPackRecordReader(private val cursor: Cursor) : Closeable { + + fun getNext(): StickerPackRecord? { + if (!cursor.moveToNext()) { + return null + } + + return getCurrent() + } + + fun getCurrent(): StickerPackRecord { + val cover = StickerRecordReader(cursor).getCurrent() + + return StickerPackRecord( + packId = cursor.requireNonNullString(PACK_ID), + packKey = cursor.requireNonNullString(PACK_KEY), + title = cursor.requireNonNullString(PACK_TITLE), + author = cursor.requireNonNullString(PACK_AUTHOR), + cover = cover, + isInstalled = cursor.requireBoolean(INSTALLED) + ) + } + + override fun close() { + cursor.close() + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java deleted file mode 100644 index 1f69a78dbd..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.java +++ /dev/null @@ -1,74 +0,0 @@ -package org.thoughtcrime.securesms.database.model; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -public class IncomingSticker { - - private final String packKey; - private final String packId; - private final String packTitle; - private final String packAuthor; - private final int stickerId; - private final String emoji; - private final String contentType; - private final boolean isCover; - private final boolean isInstalled; - - public IncomingSticker(@NonNull String packId, - @NonNull String packKey, - @NonNull String packTitle, - @NonNull String packAuthor, - int stickerId, - @NonNull String emoji, - @Nullable String contentType, - boolean isCover, - boolean isInstalled) - { - this.packId = packId; - this.packKey = packKey; - this.packTitle = packTitle; - this.packAuthor = packAuthor; - this.stickerId = stickerId; - this.emoji = emoji; - this.contentType = contentType; - this.isCover = isCover; - this.isInstalled = isInstalled; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackTitle() { - return packTitle; - } - - public @NonNull String getPackAuthor() { - return packAuthor; - } - - public int getStickerId() { - return stickerId; - } - - public @NonNull String getEmoji() { - return emoji; - } - - public @Nullable String getContentType() { - return contentType; - } - - public boolean isCover() { - return isCover; - } - - public boolean isInstalled() { - return isInstalled; - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.kt new file mode 100644 index 0000000000..49aa65bfb7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/IncomingSticker.kt @@ -0,0 +1,13 @@ +package org.thoughtcrime.securesms.database.model + +data class IncomingSticker( + val packId: String, + val packKey: String, + val packTitle: String, + val packAuthor: String, + val stickerId: Int, + val emoji: String, + val contentType: String?, + val isCover: Boolean, + val isInstalled: Boolean +) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java deleted file mode 100644 index 0e7b89eff8..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.thoughtcrime.securesms.database.model; - -import android.text.TextUtils; - -import androidx.annotation.NonNull; - -import org.thoughtcrime.securesms.database.StickerTable; - -import java.util.Objects; -import java.util.Optional; - -/** - * Represents a record for a sticker pack in the {@link StickerTable}. - */ -public final class StickerPackRecord { - - private final String packId; - private final String packKey; - private final Optional title; - private final Optional author; - private final StickerRecord cover; - private final boolean installed; - - public StickerPackRecord(@NonNull String packId, - @NonNull String packKey, - @NonNull String title, - @NonNull String author, - @NonNull StickerRecord cover, - boolean installed) - { - this.packId = packId; - this.packKey = packKey; - this.title = TextUtils.isEmpty(title) ? Optional.empty() : Optional.of(title); - this.author = TextUtils.isEmpty(author) ? Optional.empty() : Optional.of(author); - this.cover = cover; - this.installed = installed; - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public @NonNull Optional getTitle() { - return title; - } - - public @NonNull Optional getAuthor() { - return author; - } - - public @NonNull StickerRecord getCover() { - return cover; - } - - public boolean isInstalled() { - return installed; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StickerPackRecord record = (StickerPackRecord) o; - return installed == record.installed && - packId.equals(record.packId) && - packKey.equals(record.packKey) && - title.equals(record.title) && - author.equals(record.author) && - cover.equals(record.cover); - } - - @Override - public int hashCode() { - return Objects.hash(packId, packKey, title, author, cover, installed); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt new file mode 100644 index 0000000000..4123c905aa --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerPackRecord.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.database.model + +import java.util.Optional + +/** + * Represents a record for a sticker pack in the [StickerTable]. + */ +data class StickerPackRecord( + @JvmField val packId: String, + @JvmField val packKey: String, + @JvmField val title: String, + @JvmField val author: String, + @JvmField val cover: StickerRecord, + @JvmField val isInstalled: Boolean +) { + @JvmField + val titleOptional: Optional = if (title.isBlank()) Optional.empty() else Optional.of(title) + + @JvmField + val authorOptional: Optional = if (author.isBlank()) Optional.empty() else Optional.of(author) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java deleted file mode 100644 index 95f91961ce..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.thoughtcrime.securesms.database.model; - -import android.net.Uri; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import org.thoughtcrime.securesms.database.StickerTable; -import org.thoughtcrime.securesms.mms.PartAuthority; -import org.thoughtcrime.securesms.util.MediaUtil; -import org.thoughtcrime.securesms.util.Util; - -import java.util.Objects; - -/** - * Represents a record for a sticker pack in the {@link StickerTable}. - */ -public final class StickerRecord { - - private final long rowId; - private final String packId; - private final String packKey; - private final int stickerId; - private final String emoji; - private final String contentType; - private final long size; - private final boolean isCover; - - public StickerRecord(long rowId, - @NonNull String packId, - @NonNull String packKey, - int stickerId, - @NonNull String emoji, - @Nullable String contentType, - long size, - boolean isCover) - { - this.rowId = rowId; - this.packId = packId; - this.packKey = packKey; - this.stickerId = stickerId; - this.emoji = emoji; - this.contentType = contentType; - this.size = size; - this.isCover = isCover; - } - - public long getRowId() { - return rowId; - } - - public @NonNull String getPackId() { - return packId; - } - - public @NonNull String getPackKey() { - return packKey; - } - - public int getStickerId() { - return stickerId; - } - - public @NonNull Uri getUri() { - return PartAuthority.getStickerUri(rowId); - } - - public @NonNull String getEmoji() { - return emoji; - } - - public @NonNull String getContentType() { - return Util.isEmpty(contentType) ? MediaUtil.IMAGE_WEBP : contentType; - } - - public long getSize() { - return size; - } - - public boolean isCover() { - return isCover; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StickerRecord that = (StickerRecord) o; - return rowId == that.rowId && - stickerId == that.stickerId && - size == that.size && - isCover == that.isCover && - packId.equals(that.packId) && - packKey.equals(that.packKey) && - emoji.equals(that.emoji) && - Objects.equals(contentType, that.contentType); - } - - @Override - public int hashCode() { - return Objects.hash(rowId, packId, packKey, stickerId, emoji, contentType, size, isCover); - } -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.kt b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.kt new file mode 100644 index 0000000000..b5178ee7da --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/StickerRecord.kt @@ -0,0 +1,21 @@ +package org.thoughtcrime.securesms.database.model + +import android.net.Uri +import org.thoughtcrime.securesms.mms.PartAuthority + +/** + * Represents a record for a sticker pack in the [StickerTable]. + */ +data class StickerRecord( + @JvmField val rowId: Long, + @JvmField val packId: String, + @JvmField val packKey: String, + @JvmField val stickerId: Int, + @JvmField val emoji: String, + @JvmField val contentType: String, + @JvmField val size: Long, + @JvmField val isCover: Boolean +) { + @JvmField + val uri: Uri = PartAuthority.getStickerUri(rowId) +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java index 42141dce64..d209ccea10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceStickerPackSyncJob.java @@ -72,8 +72,8 @@ public class MultiDeviceStickerPackSyncJob extends BaseJob { try (StickerPackRecordReader reader = new StickerPackRecordReader(SignalDatabase.stickers().getInstalledStickerPacks())) { StickerPackRecord pack; while ((pack = reader.getNext()) != null) { - byte[] packIdBytes = Hex.fromStringCondensed(pack.getPackId()); - byte[] packKeyBytes = Hex.fromStringCondensed(pack.getPackKey()); + byte[] packIdBytes = Hex.fromStringCondensed(pack.packId); + byte[] packKeyBytes = Hex.fromStringCondensed(pack.packKey); operations.add(new StickerPackOperationMessage(packIdBytes, packKeyBytes, StickerPackOperationMessage.Type.INSTALL)); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 1a32053aeb..bd3f852feb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -426,7 +426,7 @@ public abstract class PushSendJob extends SendJob { byte[] packKey = Hex.fromStringCondensed(stickerAttachment.stickerLocator.packKey); int stickerId = stickerAttachment.stickerLocator.stickerId; StickerRecord record = SignalDatabase.stickers().getSticker(stickerAttachment.stickerLocator.packId, stickerId, false); - String emoji = record != null ? record.getEmoji() : null; + String emoji = record != null ? record.emoji : null; SignalServiceAttachment attachment = getAttachmentPointerFor(stickerAttachment); return Optional.of(new SignalServiceDataMessage.Sticker(packId, packKey, stickerId, emoji, attachment)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java index 6947949264..4f3ed9a489 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StickerDownloadJob.java @@ -82,7 +82,7 @@ public class StickerDownloadJob extends BaseJob { StickerRecord stickerRecord = db.getSticker(sticker.getPackId(), sticker.getStickerId(), sticker.isCover()); if (stickerRecord != null) { - try (InputStream stream = PartAuthority.getAttachmentStream(context, stickerRecord.getUri())) { + try (InputStream stream = PartAuthority.getAttachmentStream(context, stickerRecord.uri)) { if (stream != null) { Log.w(TAG, "Sticker already downloaded."); return; diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerKeyboardRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerKeyboardRepository.kt index 91dc726446..e31787e960 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerKeyboardRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerKeyboardRepository.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.keyboard.sticker import android.net.Uri import org.signal.core.util.concurrent.SignalExecutors +import org.signal.core.util.nullIfBlank import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.database.StickerTable import org.thoughtcrime.securesms.database.StickerTable.StickerPackRecordReader @@ -18,11 +19,11 @@ class StickerKeyboardRepository(private val stickerTable: StickerTable) { SignalExecutors.BOUNDED.execute { val packs: MutableList = mutableListOf() - StickerPackRecordReader(stickerTable.installedStickerPacks).use { reader -> - var pack: StickerPackRecord? = reader.next + StickerPackRecordReader(stickerTable.getInstalledStickerPacks()).use { reader -> + var pack: StickerPackRecord? = reader.getNext() while (pack != null) { - packs += KeyboardStickerPack(packId = pack.packId, title = pack.title.orElse(null), coverUri = pack.cover.uri) - pack = reader.next + packs += KeyboardStickerPack(packId = pack.packId, title = pack.title.nullIfBlank(), coverUri = pack.cover.uri) + pack = reader.getNext() } } @@ -30,10 +31,10 @@ class StickerKeyboardRepository(private val stickerTable: StickerTable) { val stickers: MutableList = mutableListOf() StickerRecordReader(stickerTable.getStickersForPack(p.packId)).use { reader -> - var sticker: StickerRecord? = reader.next + var sticker: StickerRecord? = reader.getNext() while (sticker != null) { stickers.add(sticker) - sticker = reader.next + sticker = reader.getNext() } } @@ -52,10 +53,10 @@ class StickerKeyboardRepository(private val stickerTable: StickerTable) { val recentStickers: MutableList = mutableListOf() StickerRecordReader(stickerTable.getRecentlyUsedStickers(RECENT_LIMIT)).use { reader -> - var recentSticker: StickerRecord? = reader.next + var recentSticker: StickerRecord? = reader.getNext() while (recentSticker != null) { recentStickers.add(recentSticker) - recentSticker = reader.next + recentSticker = reader.getNext() } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerSearchRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerSearchRepository.kt index fc56298f4b..871018cfb5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerSearchRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/keyboard/sticker/StickerSearchRepository.kt @@ -44,10 +44,10 @@ class StickerSearchRepository { private fun StickerRecordReader.readAll(): List { val stickers: MutableList = mutableListOf() use { reader -> - var record: StickerRecord? = reader.next + var record: StickerRecord? = reader.getNext() while (record != null) { stickers.add(record) - record = reader.next + record = reader.getNext() } } return stickers diff --git a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorStickerSelectActivity.java b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorStickerSelectActivity.java index 85347e5b98..62ec008407 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorStickerSelectActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/scribbles/ImageEditorStickerSelectActivity.java @@ -56,10 +56,10 @@ public final class ImageEditorStickerSelectActivity extends AppCompatActivity im @Override public void onStickerSelected(@NonNull StickerRecord sticker) { Intent intent = new Intent(); - intent.setData(sticker.getUri()); + intent.setData(sticker.uri); setResult(RESULT_OK, intent); - SignalExecutors.BOUNDED.execute(() -> SignalDatabase.stickers().updateStickerLastUsedTime(sticker.getRowId(), System.currentTimeMillis())); + SignalExecutors.BOUNDED.execute(() -> SignalDatabase.stickers().updateStickerLastUsedTime(sticker.rowId, System.currentTimeMillis())); ViewUtil.hideKeyboard(this, findViewById(android.R.id.content)); finish(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java index 5fae5f2143..dc82a103d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementAdapter.java @@ -197,7 +197,7 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter eventListener.onStickerPackUninstallClicked(stickerPack.getPackId(), stickerPack.getPackKey())); + actionButton.setOnClickListener(v -> eventListener.onStickerPackUninstallClicked(stickerPack.packId, stickerPack.packKey)); shareButton.setVisibility(View.VISIBLE); shareButtonImage.setVisibility(View.VISIBLE); - shareButton.setOnClickListener(v -> eventListener.onStickerPackShareClicked(stickerPack.getPackId(), stickerPack.getPackKey())); + shareButton.setOnClickListener(v -> eventListener.onStickerPackShareClicked(stickerPack.packId, stickerPack.packKey)); } else { actionButtonImage.setImageResource(R.drawable.symbol_arrow_down_24); - actionButton.setOnClickListener(v -> eventListener.onStickerPackInstallClicked(stickerPack.getPackId(), stickerPack.getPackKey())); + actionButton.setOnClickListener(v -> eventListener.onStickerPackInstallClicked(stickerPack.packId, stickerPack.packKey)); shareButton.setVisibility(View.GONE); shareButtonImage.setVisibility(View.GONE); shareButton.setOnClickListener(null); } - itemView.setOnClickListener(v -> eventListener.onStickerPackClicked(stickerPack.getPackId(), stickerPack.getPackKey())); + itemView.setOnClickListener(v -> eventListener.onStickerPackClicked(stickerPack.packId, stickerPack.packKey)); } void recycle() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java index 3d83764a85..6946686853 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerManagementRepository.java @@ -61,9 +61,9 @@ final class StickerManagementRepository { try (StickerPackRecordReader reader = new StickerPackRecordReader(stickerDatabase.getAllStickerPacks())) { StickerPackRecord record; while ((record = reader.getNext()) != null) { - if (record.isInstalled()) { + if (record.isInstalled) { installedPacks.add(record); - } else if (BlessedPacks.contains(record.getPackId())) { + } else if (BlessedPacks.contains(record.packId)) { blessedPacks.add(record); } else { availablePacks.add(record); diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java index 69e584e5a2..cf574297b7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerPackPreviewRepository.java @@ -58,18 +58,18 @@ public final class StickerPackPreviewRepository { private Optional getManifestFromDatabase(@NonNull String packId) { StickerPackRecord record = stickerDatabase.getStickerPack(packId); - if (record != null && record.isInstalled()) { - StickerManifest.Sticker cover = toSticker(record.getCover()); + if (record != null && record.isInstalled) { + StickerManifest.Sticker cover = toSticker(record.cover); List stickers = getStickersFromDatabase(packId); - StickerManifest manifest = new StickerManifest(record.getPackId(), - record.getPackKey(), - record.getTitle(), - record.getAuthor(), + StickerManifest manifest = new StickerManifest(record.packId, + record.packKey, + record.titleOptional, + record.authorOptional, Optional.of(cover), stickers); - return Optional.of(new StickerManifestResult(manifest, record.isInstalled())); + return Optional.of(new StickerManifestResult(manifest, record.isInstalled)); } return Optional.empty(); @@ -131,7 +131,7 @@ public final class StickerPackPreviewRepository { } private StickerManifest.Sticker toSticker(@NonNull StickerRecord record) { - return new StickerManifest.Sticker(record.getPackId(), record.getPackKey(), record.getStickerId(), record.getEmoji(), record.getContentType(), record.getUri()); + return new StickerManifest.Sticker(record.packId, record.packKey, record.stickerId, record.emoji, record.contentType, record.uri); } static class StickerManifestResult { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRolloverTouchListener.java b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRolloverTouchListener.java index 7648f1bb34..024e81d82d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRolloverTouchListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/stickers/StickerRolloverTouchListener.java @@ -81,7 +81,7 @@ public class StickerRolloverTouchListener implements RecyclerView.OnItemTouchLis public void enterHoverMode(@NonNull RecyclerView recyclerView, @NonNull KeyboardStickerListAdapter.Sticker sticker) { this.hoverMode = true; - showSticker(recyclerView, sticker.getUri(), sticker.getStickerRecord().getEmoji()); + showSticker(recyclerView, sticker.getUri(), sticker.getStickerRecord().emoji); } private void exitHoverMode() {