Convert StickerTable to kotlin.

This commit is contained in:
Greyson Parrelli 2025-01-07 16:59:45 -05:00
parent 1509e3ed79
commit 465c852e8b
23 changed files with 574 additions and 819 deletions

View file

@ -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));

View file

@ -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)});
}

View file

@ -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()
}
}
}

View file

@ -66,7 +66,7 @@ public class ConversationStickerSuggestionAdapter extends RecyclerView.Adapter<C
}
void bind(@NonNull RequestManager requestManager, @NonNull EventListener eventListener, @NonNull StickerRecord sticker) {
requestManager.load(new DecryptableUri(sticker.getUri()))
requestManager.load(new DecryptableUri(sticker.uri))
.transition(DrawableTransitionOptions.withCrossFade())
.fitCenter()
.into(image);

View file

@ -930,7 +930,7 @@ class AttachmentTable(
cursor.requireString(THUMBNAIL_FILE)?.let { filesInDb += it }
}
filesInDb += SignalDatabase.stickers.allStickerFiles
filesInDb += SignalDatabase.stickers.getAllStickerFiles()
val onDiskButNotInDatabase: Set<String> = filesOnDisk - filesInDb

View file

@ -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<String> getAllStickerFiles() {
SQLiteDatabase db = databaseHelper.getSignalReadableDatabase();
Set<String> 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<StickerPackRecord> 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<byte[], OutputStream> 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();
}
}
}
}

View file

@ -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<String> = 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<String> {
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<StickerPackRecord>) {
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()
}
}
}

View file

@ -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;
}
}

View file

@ -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
)

View file

@ -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<String> title;
private final Optional<String> 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<String> getTitle() {
return title;
}
public @NonNull Optional<String> 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);
}
}

View file

@ -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<String> = if (title.isBlank()) Optional.empty() else Optional.of(title)
@JvmField
val authorOptional: Optional<String> = if (author.isBlank()) Optional.empty() else Optional.of(author)
}

View file

@ -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);
}
}

View file

@ -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)
}

View file

@ -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));
}

View file

@ -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));

View file

@ -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;

View file

@ -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<KeyboardStickerPack> = 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<StickerRecord> = 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<StickerRecord> = 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()
}
}

View file

@ -44,10 +44,10 @@ class StickerSearchRepository {
private fun StickerRecordReader.readAll(): List<StickerRecord> {
val stickers: MutableList<StickerRecord> = 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

View file

@ -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();
}

View file

@ -197,7 +197,7 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
} else if (records.isEmpty()) {
return idGenerator.getId(tag + "_" + STABLE_ID_TEXT);
} else {
return idGenerator.getId(records.get(localPosition - 1).getPackId());
return idGenerator.getId(records.get(localPosition - 1).packId);
}
}
@ -264,38 +264,38 @@ final class StickerManagementAdapter extends SectionedRecyclerViewAdapter<String
boolean lastInList,
boolean allowApngAnimation)
{
SpannableStringBuilder titleBuilder = new SpannableStringBuilder(stickerPack.getTitle().orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_untitled)));
if (BlessedPacks.contains(stickerPack.getPackId())) {
SpannableStringBuilder titleBuilder = new SpannableStringBuilder(stickerPack.titleOptional.orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_untitled)));
if (BlessedPacks.contains(stickerPack.packId)) {
titleBuilder.append(blessedBadge);
}
title.setText(titleBuilder);
author.setText(stickerPack.getAuthor().orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_unknown)));
author.setText(stickerPack.authorOptional.orElse(itemView.getResources().getString(R.string.StickerManagementAdapter_unknown)));
divider.setVisibility(lastInList ? View.GONE : View.VISIBLE);
requestManager.load(new DecryptableUri(stickerPack.getCover().getUri()))
requestManager.load(new DecryptableUri(stickerPack.cover.uri))
.transition(DrawableTransitionOptions.withCrossFade())
.fitCenter()
.set(ApngOptions.ANIMATE, allowApngAnimation)
.into(cover);
if (stickerPack.isInstalled()) {
if (stickerPack.isInstalled) {
actionButtonImage.setImageResource(R.drawable.ic_x);
actionButton.setOnClickListener(v -> 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() {

View file

@ -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);

View file

@ -58,18 +58,18 @@ public final class StickerPackPreviewRepository {
private Optional<StickerManifestResult> 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<StickerManifest.Sticker> 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 {

View file

@ -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() {