Convert MessageTable to kotlin.

This commit is contained in:
Greyson Parrelli
2023-03-11 15:35:14 -05:00
parent c2a76c4313
commit 90cc672c37
19 changed files with 5245 additions and 5682 deletions

View File

@@ -18,7 +18,7 @@ class SmsSettingsRepository(
@WorkerThread @WorkerThread
private fun checkInsecureMessageCount(): SmsExportState? { private fun checkInsecureMessageCount(): SmsExportState? {
val totalSmsMmsCount = smsDatabase.insecureMessageCount + mmsDatabase.insecureMessageCount val totalSmsMmsCount = smsDatabase.getInsecureMessageCount() + mmsDatabase.getInsecureMessageCount()
return if (totalSmsMmsCount == 0) { return if (totalSmsMmsCount == 0) {
SmsExportState.NO_SMS_MESSAGES_IN_DATABASE SmsExportState.NO_SMS_MESSAGES_IN_DATABASE
@@ -29,7 +29,7 @@ class SmsSettingsRepository(
@WorkerThread @WorkerThread
private fun checkUnexportedInsecureMessageCount(): SmsExportState { private fun checkUnexportedInsecureMessageCount(): SmsExportState {
val totalUnexportedCount = smsDatabase.unexportedInsecureMessagesCount + mmsDatabase.unexportedInsecureMessagesCount val totalUnexportedCount = smsDatabase.getUnexportedInsecureMessagesCount() + mmsDatabase.getUnexportedInsecureMessagesCount()
return if (totalUnexportedCount > 0) { return if (totalUnexportedCount > 0) {
SmsExportState.HAS_UNEXPORTED_MESSAGES SmsExportState.HAS_UNEXPORTED_MESSAGES

View File

@@ -1193,7 +1193,6 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
if (message.getScheduledDate() != -1) { if (message.getScheduledDate() != -1) {
return; return;
} }
MessageRecord messageRecord = MessageTable.readerFor(message, threadId).getCurrent();
if (getListAdapter() != null) { if (getListAdapter() != null) {
setLastSeen(0); setLastSeen(0);

File diff suppressed because it is too large Load Diff

View File

@@ -128,7 +128,7 @@ class SearchTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa
* Be smart about where you call this. * Be smart about where you call this.
*/ */
fun rebuildIndex(batchSize: Long = 10_000L) { fun rebuildIndex(batchSize: Long = 10_000L) {
val maxId: Long = SignalDatabase.messages.nextId val maxId: Long = SignalDatabase.messages.getNextId()
Log.i(TAG, "Re-indexing. Operating on ID's 1-$maxId in steps of $batchSize.") Log.i(TAG, "Re-indexing. Operating on ID's 1-$maxId in steps of $batchSize.")

View File

@@ -38,7 +38,7 @@ class SignalSmsExportReader(
} }
fun getCount(): Int { fun getCount(): Int {
return messageTable.unexportedInsecureMessagesCount return messageTable.getUnexportedInsecureMessagesCount()
} }
override fun close() { override fun close() {
@@ -51,7 +51,7 @@ class SignalSmsExportReader(
messageReader = null messageReader = null
val refreshedMmsReader = MessageTable.mmsReaderFor(messageTable.getUnexportedInsecureMessages(CURSOR_LIMIT)) val refreshedMmsReader = MessageTable.mmsReaderFor(messageTable.getUnexportedInsecureMessages(CURSOR_LIMIT))
if (refreshedMmsReader.count > 0) { if (refreshedMmsReader.getCount() > 0) {
messageReader = refreshedMmsReader messageReader = refreshedMmsReader
return return
} else { } else {
@@ -88,14 +88,14 @@ class SignalSmsExportReader(
try { try {
return if (messageIterator?.hasNext() == true) { return if (messageIterator?.hasNext() == true) {
record = messageIterator!!.next() record = messageIterator!!.next()
readExportableMmsMessageFromRecord(record, messageReader!!.messageExportStateForCurrentRecord) readExportableMmsMessageFromRecord(record, messageReader!!.getMessageExportStateForCurrentRecord())
} else { } else {
throw NoSuchElementException() throw NoSuchElementException()
} }
} catch (e: Throwable) { } catch (e: Throwable) {
if (e.cause is JSONException) { if (e.cause is JSONException) {
Log.w(TAG, "Error processing attachment json, skipping message.", e) Log.w(TAG, "Error processing attachment json, skipping message.", e)
return ExportableMessage.Skip(messageReader!!.currentId) return ExportableMessage.Skip(messageReader!!.getCurrentId())
} }
Log.w(TAG, "Error processing message: isMms: ${record?.isMms} type: ${record?.type}") Log.w(TAG, "Error processing message: isMms: ${record?.isMms} type: ${record?.type}")

View File

@@ -1,118 +0,0 @@
package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.attachments.Attachment;
import org.thoughtcrime.securesms.database.model.Mention;
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import java.util.Collections;
import java.util.List;
public class QuoteModel {
private final long id;
private final RecipientId author;
private final String text;
private final boolean missing;
private final List<Attachment> attachments;
private final List<Mention> mentions;
private final Type type;
private final BodyRangeList bodyRanges;
public QuoteModel(long id,
@NonNull RecipientId author,
String text,
boolean missing,
@Nullable List<Attachment> attachments,
@Nullable List<Mention> mentions,
@NonNull Type type,
@Nullable BodyRangeList bodyRanges)
{
this.id = id;
this.author = author;
this.text = text;
this.missing = missing;
this.attachments = attachments;
this.mentions = mentions != null ? mentions : Collections.emptyList();
this.type = type;
this.bodyRanges = bodyRanges;
}
public long getId() {
return id;
}
public RecipientId getAuthor() {
return author;
}
public String getText() {
return text;
}
public boolean isOriginalMissing() {
return missing;
}
public List<Attachment> getAttachments() {
return attachments;
}
public @NonNull List<Mention> getMentions() {
return mentions;
}
public Type getType() {
return type;
}
public @Nullable BodyRangeList getBodyRanges() {
return bodyRanges;
}
public enum Type {
NORMAL(0, SignalServiceDataMessage.Quote.Type.NORMAL),
GIFT_BADGE(1, SignalServiceDataMessage.Quote.Type.GIFT_BADGE);
private final int code;
private final SignalServiceDataMessage.Quote.Type dataMessageType;
Type(int code, @NonNull SignalServiceDataMessage.Quote.Type dataMessageType) {
this.code = code;
this.dataMessageType = dataMessageType;
}
public int getCode() {
return code;
}
public @NonNull SignalServiceDataMessage.Quote.Type getDataMessageType() {
return dataMessageType;
}
public static Type fromCode(int code) {
for (final Type value : values()) {
if (value.code == code) {
return value;
}
}
throw new IllegalArgumentException("Invalid code: " + code);
}
public static Type fromDataMessageType(@NonNull SignalServiceDataMessage.Quote.Type dataMessageType) {
for (final Type value : values()) {
if (value.dataMessageType == dataMessageType) {
return value;
}
}
return NORMAL;
}
}
}

View File

@@ -0,0 +1,52 @@
package org.thoughtcrime.securesms.mms
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.database.model.Mention
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
class QuoteModel(
val id: Long,
val author: RecipientId,
val text: String,
val isOriginalMissing: Boolean,
val attachments: List<Attachment>,
mentions: List<Mention>?,
val type: Type,
val bodyRanges: BodyRangeList?
) {
val mentions: List<Mention>
init {
this.mentions = mentions ?: emptyList()
}
enum class Type(val code: Int, val dataMessageType: SignalServiceDataMessage.Quote.Type) {
NORMAL(0, SignalServiceDataMessage.Quote.Type.NORMAL),
GIFT_BADGE(1, SignalServiceDataMessage.Quote.Type.GIFT_BADGE);
companion object {
@JvmStatic
fun fromCode(code: Int): Type {
for (value in values()) {
if (value.code == code) {
return value
}
}
throw IllegalArgumentException("Invalid code: $code")
}
@JvmStatic
fun fromDataMessageType(dataMessageType: SignalServiceDataMessage.Quote.Type): Type {
for (value in values()) {
if (value.dataMessageType === dataMessageType) {
return value
}
}
return NORMAL
}
}
}
}

View File

@@ -33,7 +33,7 @@ object NotificationStateProvider {
} }
MessageTable.mmsReaderFor(unreadMessages).use { reader -> MessageTable.mmsReaderFor(unreadMessages).use { reader ->
var record: MessageRecord? = reader.next var record: MessageRecord? = reader.getNext()
while (record != null) { while (record != null) {
val threadRecipient: Recipient? = SignalDatabase.threads.getRecipientForThreadId(record.threadId) val threadRecipient: Recipient? = SignalDatabase.threads.getRecipientForThreadId(record.threadId)
if (threadRecipient != null) { if (threadRecipient != null) {
@@ -73,7 +73,7 @@ object NotificationStateProvider {
) )
} }
try { try {
record = reader.next record = reader.getNext()
} catch (e: IllegalStateException) { } catch (e: IllegalStateException) {
// XXX Weird SQLCipher bug that's being investigated // XXX Weird SQLCipher bug that's being investigated
record = null record = null

View File

@@ -34,7 +34,7 @@ class ConversationListTabRepository {
fun getNumberOfUnseenStories(): Observable<Long> { fun getNumberOfUnseenStories(): Observable<Long> {
return Observable.create<Long> { emitter -> return Observable.create<Long> { emitter ->
fun refresh() { fun refresh() {
emitter.onNext(SignalDatabase.messages.unreadStoryThreadRecipientIds.map { Recipient.resolved(it) }.filterNot { it.shouldHideStory() }.size.toLong()) emitter.onNext(SignalDatabase.messages.getUnreadStoryThreadRecipientIds().map { Recipient.resolved(it) }.filterNot { it.shouldHideStory() }.size.toLong())
} }
val listener = DatabaseObserver.Observer { val listener = DatabaseObserver.Observer {

View File

@@ -38,7 +38,7 @@ open class StoryViewerRepository {
} }
fun getStories(hiddenStories: Boolean, isOutgoingOnly: Boolean): Single<List<RecipientId>> { fun getStories(hiddenStories: Boolean, isOutgoingOnly: Boolean): Single<List<RecipientId>> {
return Single.create<List<RecipientId>> { emitter -> return Single.create { emitter ->
val myStoriesId = SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY) val myStoriesId = SignalDatabase.recipients.getOrInsertFromDistributionListId(DistributionListId.MY_STORY)
val myStories = Recipient.resolved(myStoriesId) val myStories = Recipient.resolved(myStoriesId)
val releaseChannelId = SignalStore.releaseChannelValues().releaseChannelRecipientId val releaseChannelId = SignalStore.releaseChannelValues().releaseChannelRecipientId

View File

@@ -20,7 +20,7 @@ class StoryGroupReplyDataSource(private val parentStoryId: Long) : PagedDataSour
cursor.moveToPosition(start - 1) cursor.moveToPosition(start - 1)
val mmsReader = MessageTable.MmsReader(cursor) val mmsReader = MessageTable.MmsReader(cursor)
while (cursor.moveToNext() && cursor.position < start + length) { while (cursor.moveToNext() && cursor.position < start + length) {
results.add(readRowFromRecord(mmsReader.current as MmsMessageRecord)) results.add(readRowFromRecord(mmsReader.getCurrent() as MmsMessageRecord))
} }
} }

View File

@@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.testing
import android.content.ContentValues import android.content.ContentValues
import android.database.Cursor import android.database.Cursor
import androidx.sqlite.db.SupportSQLiteQuery
import org.signal.core.util.toAndroidQuery
import java.util.Locale import java.util.Locale
import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase import android.database.sqlite.SQLiteDatabase as AndroidSQLiteDatabase
import android.database.sqlite.SQLiteTransactionListener as AndroidSQLiteTransactionListener import android.database.sqlite.SQLiteTransactionListener as AndroidSQLiteTransactionListener
@@ -49,6 +51,11 @@ class ProxySignalSQLiteDatabase(private val database: AndroidSQLiteDatabase) : S
return database.queryWithFactory(null, distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit) return database.queryWithFactory(null, distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit)
} }
override fun query(query: SupportSQLiteQuery): Cursor? {
val converted = query.toAndroidQuery()
return database.rawQuery(converted.where, converted.whereArgs)
}
override fun query(table: String?, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?): Cursor { override fun query(table: String?, columns: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, groupBy: String?, having: String?, orderBy: String?): Cursor {
return database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy) return database.query(table, columns, selection, selectionArgs, groupBy, having, orderBy)
} }

View File

@@ -162,4 +162,12 @@ inline fun <T> Cursor.firstOrNull(predicate: (T) -> Boolean = { true }, mapper:
return null return null
} }
inline fun Cursor.forEach(operation: (Cursor) -> Unit) {
use {
while (moveToNext()) {
operation(this)
}
}
}
fun Boolean.toInt(): Int = if (this) 1 else 0 fun Boolean.toInt(): Int = if (this) 1 else 0

View File

@@ -70,6 +70,10 @@ fun SupportSQLiteDatabase.delete(tableName: String): DeleteBuilderPart1 {
return DeleteBuilderPart1(this, tableName) return DeleteBuilderPart1(this, tableName)
} }
fun SupportSQLiteDatabase.insertInto(tableName: String): InsertBuilderPart1 {
return InsertBuilderPart1(this, tableName)
}
class SelectBuilderPart1( class SelectBuilderPart1(
private val db: SupportSQLiteDatabase, private val db: SupportSQLiteDatabase,
private val columns: Array<String> private val columns: Array<String>
@@ -117,6 +121,10 @@ class SelectBuilderPart3(
return SelectBuilderPart4b(db, columns, tableName, where, whereArgs, limit.toString()) return SelectBuilderPart4b(db, columns, tableName, where, whereArgs, limit.toString())
} }
fun limit(limit: String): SelectBuilderPart4b {
return SelectBuilderPart4b(db, columns, tableName, where, whereArgs, limit)
}
fun run(): Cursor { fun run(): Cursor {
return db.query( return db.query(
SupportSQLiteQueryBuilder SupportSQLiteQueryBuilder
@@ -140,6 +148,10 @@ class SelectBuilderPart4a(
return SelectBuilderPart5(db, columns, tableName, where, whereArgs, orderBy, limit.toString()) return SelectBuilderPart5(db, columns, tableName, where, whereArgs, orderBy, limit.toString())
} }
fun limit(limit: String): SelectBuilderPart5 {
return SelectBuilderPart5(db, columns, tableName, where, whereArgs, orderBy, limit)
}
fun run(): Cursor { fun run(): Cursor {
return db.query( return db.query(
SupportSQLiteQueryBuilder SupportSQLiteQueryBuilder
@@ -220,6 +232,10 @@ class UpdateBuilderPart2(
return UpdateBuilderPart3(db, tableName, values, where, SqlUtil.buildArgs(*whereArgs)) return UpdateBuilderPart3(db, tableName, values, where, SqlUtil.buildArgs(*whereArgs))
} }
fun where(@Language("sql") where: String, whereArgs: Array<String>): UpdateBuilderPart3 {
return UpdateBuilderPart3(db, tableName, values, where, whereArgs)
}
fun run(conflictStrategy: Int = SQLiteDatabase.CONFLICT_NONE): Int { fun run(conflictStrategy: Int = SQLiteDatabase.CONFLICT_NONE): Int {
return db.update(tableName, conflictStrategy, values, null, null) return db.update(tableName, conflictStrategy, values, null, null)
} }
@@ -246,6 +262,10 @@ class DeleteBuilderPart1(
return DeleteBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs)) return DeleteBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs))
} }
fun where(@Language("sql") where: String, whereArgs: Array<String>): DeleteBuilderPart2 {
return DeleteBuilderPart2(db, tableName, where, whereArgs)
}
fun run(): Int { fun run(): Int {
return db.delete(tableName, null, null) return db.delete(tableName, null, null)
} }
@@ -271,6 +291,10 @@ class ExistsBuilderPart1(
return ExistsBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs)) return ExistsBuilderPart2(db, tableName, where, SqlUtil.buildArgs(*whereArgs))
} }
fun where(@Language("sql") where: String, whereArgs: Array<String>): ExistsBuilderPart2 {
return ExistsBuilderPart2(db, tableName, where, whereArgs)
}
fun run(): Boolean { fun run(): Boolean {
return db.query("SELECT EXISTS(SELECT 1 FROM $tableName)", null).use { cursor -> return db.query("SELECT EXISTS(SELECT 1 FROM $tableName)", null).use { cursor ->
cursor.moveToFirst() && cursor.getInt(0) == 1 cursor.moveToFirst() && cursor.getInt(0) == 1
@@ -290,3 +314,26 @@ class ExistsBuilderPart2(
} }
} }
} }
class InsertBuilderPart1(
private val db: SupportSQLiteDatabase,
private val tableName: String
) {
fun values(values: ContentValues): InsertBuilderPart2 {
return InsertBuilderPart2(db, tableName, values)
}
fun values(vararg values: Pair<String, Any?>): InsertBuilderPart2 {
return InsertBuilderPart2(db, tableName, contentValuesOf(*values))
}
}
class InsertBuilderPart2(
private val db: SupportSQLiteDatabase,
private val tableName: String,
private val values: ContentValues
) {
fun run(conflictStrategy: Int = SQLiteDatabase.CONFLICT_NONE): Long {
return db.insert(tableName, conflictStrategy, values)
}
}

View File

@@ -244,12 +244,14 @@ object SqlUtil {
@JvmOverloads @JvmOverloads
@JvmStatic @JvmStatic
fun buildCollectionQuery(column: String, values: Collection<Any?>, prefix: String = "", maxSize: Int = MAX_QUERY_ARGS): List<Query> { fun buildCollectionQuery(column: String, values: Collection<Any?>, prefix: String = "", maxSize: Int = MAX_QUERY_ARGS): List<Query> {
require(!values.isEmpty()) { "Must have values!" } return if (values.isEmpty()) {
emptyList()
return values } else {
values
.chunked(maxSize) .chunked(maxSize)
.map { batch -> buildSingleCollectionQuery(column, batch, prefix) } .map { batch -> buildSingleCollectionQuery(column, batch, prefix) }
} }
}
/** /**
* A convenient way of making queries in the form: WHERE [column] IN (?, ?, ..., ?) * A convenient way of making queries in the form: WHERE [column] IN (?, ?, ..., ?)

View File

@@ -38,3 +38,7 @@ fun String.asListContains(item: String): Boolean {
fun String.toSingleLine(): String { fun String.toSingleLine(): String {
return this.trimIndent().split("\n").joinToString(separator = " ") return this.trimIndent().split("\n").joinToString(separator = " ")
} }
fun String?.emptyIfNull(): String {
return this ?: ""
}

View File

@@ -0,0 +1,47 @@
package org.signal.core.util
import androidx.sqlite.db.SupportSQLiteProgram
import androidx.sqlite.db.SupportSQLiteQuery
fun SupportSQLiteQuery.toAndroidQuery(): SqlUtil.Query {
val program = CapturingSqliteProgram(this.argCount)
this.bindTo(program)
return SqlUtil.Query(this.sql, program.args())
}
private class CapturingSqliteProgram(count: Int) : SupportSQLiteProgram {
private val args: Array<String?> = arrayOfNulls(count)
fun args(): Array<String> {
return args.filterNotNull().toTypedArray()
}
override fun close() {
}
override fun bindNull(index: Int) {
throw UnsupportedOperationException()
}
override fun bindLong(index: Int, value: Long) {
args[index - 1] = value.toString()
}
override fun bindDouble(index: Int, value: Double) {
args[index - 1] = value.toString()
}
override fun bindString(index: Int, value: String?) {
args[index - 1] = value
}
override fun bindBlob(index: Int, value: ByteArray?) {
throw UnsupportedOperationException()
}
override fun clearBindings() {
for (i in args.indices) {
args[i] = null
}
}
}

View File

@@ -18,6 +18,7 @@ import java.util.List;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE, application = Application.class) @Config(manifest = Config.NONE, application = Application.class)
@@ -164,9 +165,9 @@ public final class SqlUtilTest {
assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.get(0).getWhereArgs()); assertArrayEquals(new String[] { "1", "2", "3" }, updateQuery.get(0).getWhereArgs());
} }
@Test(expected = IllegalArgumentException.class)
public void buildCollectionQuery_none() { public void buildCollectionQuery_none() {
SqlUtil.buildCollectionQuery("a", Collections.emptyList()); List<SqlUtil.Query> results = SqlUtil.buildCollectionQuery("a", Collections.emptyList());
assertTrue(results.isEmpty());
} }
@Test @Test