mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-07 23:12:14 +00:00
Merge remote-tracking branch 'upstream/dev' into message-request-response
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt # libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt
This commit is contained in:
@@ -33,8 +33,9 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
|
||||
@@ -318,6 +319,28 @@ public class AttachmentDatabase extends Database {
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
void deleteAttachmentsForMessages(long[] mmsIds) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
Cursor cursor = null;
|
||||
String mmsIdString = StringUtils.join(mmsIds, ',');
|
||||
|
||||
try {
|
||||
cursor = database.query(TABLE_NAME, new String[] {DATA, THUMBNAIL, CONTENT_TYPE}, MMS_ID + " IN (?)",
|
||||
new String[] {mmsIdString}, null, null, null);
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
deleteAttachmentOnDisk(cursor.getString(0), cursor.getString(1), cursor.getString(2));
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
database.delete(TABLE_NAME, MMS_ID + " IN (?)", new String[] {mmsIdString});
|
||||
notifyAttachmentListeners();
|
||||
}
|
||||
|
||||
public void deleteAttachment(@NonNull AttachmentId id) {
|
||||
SQLiteDatabase database = databaseHelper.getWritableDatabase();
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.session.libsession.utilities.WindowDebouncer;
|
||||
import org.thoughtcrime.securesms.ApplicationContext;
|
||||
@@ -72,6 +72,11 @@ public abstract class Database {
|
||||
context.getContentResolver().notifyChange(DatabaseContentProviders.StickerPack.CONTENT_URI, null);
|
||||
}
|
||||
|
||||
protected void notifyRecipientListeners() {
|
||||
context.getContentResolver().notifyChange(DatabaseContentProviders.Recipient.CONTENT_URI, null);
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
protected void setNotifyConverationListeners(Cursor cursor, long threadId) {
|
||||
cursor.setNotificationUri(context.getContentResolver(), DatabaseContentProviders.Conversation.getUriForThread(threadId));
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ public class DatabaseContentProviders {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://network.loki.securesms.database.stickerpack");
|
||||
}
|
||||
|
||||
public static class Recipient extends NoopContentProvider {
|
||||
public static final Uri CONTENT_URI = Uri.parse("content://network.loki.securesms.database.recipient");
|
||||
}
|
||||
|
||||
private static abstract class NoopContentProvider extends ContentProvider {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import androidx.core.database.getStringOrNull
|
||||
import net.sqlcipher.Cursor
|
||||
import net.sqlcipher.database.SQLiteDatabase
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
import org.session.libsignal.utilities.Base64
|
||||
|
||||
fun <T> SQLiteDatabase.get(table: String, query: String?, arguments: Array<String>?, get: (Cursor) -> T): T? {
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
@@ -12,7 +11,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.session.libsession.utilities.Address;
|
||||
@@ -319,6 +318,19 @@ public class GroupDatabase extends Database implements LokiOpenGroupDatabaseProt
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public boolean hasDownloadedProfilePicture(String groupId) {
|
||||
try (Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[]{AVATAR}, GROUP_ID + " = ?",
|
||||
new String[] {groupId},
|
||||
null, null, null))
|
||||
{
|
||||
if (cursor != null && cursor.moveToNext()) {
|
||||
return !cursor.isNull(0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMembers(String groupId, List<Address> members) {
|
||||
Collections.sort(members);
|
||||
|
||||
|
||||
@@ -51,31 +51,31 @@ class GroupMemberDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
return mappings.map { it.role }
|
||||
}
|
||||
|
||||
fun addGroupMember(member: GroupMember) {
|
||||
fun setGroupMembers(members: List<GroupMember>) {
|
||||
writableDatabase.beginTransaction()
|
||||
try {
|
||||
val values = ContentValues().apply {
|
||||
put(GROUP_ID, member.groupId)
|
||||
put(PROFILE_ID, member.profileId)
|
||||
put(ROLE, member.role.name)
|
||||
val grouped = members.groupBy { it.role }
|
||||
grouped.forEach { (role, members) ->
|
||||
if (members.isEmpty()) return@forEach
|
||||
|
||||
val toDeleteQuery = "$GROUP_ID = ? AND $ROLE = ?"
|
||||
val toDeleteArgs = arrayOf(members.first().groupId, role.name)
|
||||
|
||||
writableDatabase.delete(TABLE_NAME, toDeleteQuery, toDeleteArgs)
|
||||
|
||||
members.forEach { member ->
|
||||
val values = ContentValues().apply {
|
||||
put(GROUP_ID, member.groupId)
|
||||
put(PROFILE_ID, member.profileId)
|
||||
put(ROLE, member.role.name)
|
||||
}
|
||||
val query = "$GROUP_ID = ? AND $PROFILE_ID = ?"
|
||||
val args = arrayOf(member.groupId, member.profileId)
|
||||
|
||||
writableDatabase.insertOrUpdate(TABLE_NAME, values, query, args)
|
||||
}
|
||||
writableDatabase.setTransactionSuccessful()
|
||||
}
|
||||
val query = "$GROUP_ID = ? AND $PROFILE_ID = ?"
|
||||
val args = arrayOf(member.groupId, member.profileId)
|
||||
|
||||
writableDatabase.insertOrUpdate(TABLE_NAME, values, query, args)
|
||||
writableDatabase.setTransactionSuccessful()
|
||||
} finally {
|
||||
writableDatabase.endTransaction()
|
||||
}
|
||||
}
|
||||
|
||||
fun clearGroupMemberRoles(groupId: String) {
|
||||
writableDatabase.beginTransaction()
|
||||
try {
|
||||
val query = "$GROUP_ID = ?"
|
||||
val args = arrayOf(groupId)
|
||||
writableDatabase.delete(TABLE_NAME, query, args)
|
||||
writableDatabase.setTransactionSuccessful()
|
||||
} finally {
|
||||
writableDatabase.endTransaction()
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
@@ -110,6 +110,11 @@ public class GroupReceiptDatabase extends Database {
|
||||
db.delete(TABLE_NAME, MMS_ID + " = ?", new String[] {String.valueOf(mmsId)});
|
||||
}
|
||||
|
||||
void deleteRowsForMessages(long[] mmsIds) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.delete(TABLE_NAME, MMS_ID + " IN (?)", new String[] {StringUtils.join(mmsIds, ',')});
|
||||
}
|
||||
|
||||
void deleteAllRows() {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.delete(TABLE_NAME, null, null);
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.thoughtcrime.securesms.jobmanager.persistence.ConstraintSpec;
|
||||
|
||||
@@ -300,6 +300,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
val lastHash = database.insertOrUpdate(lastMessageHashValueTable2, row, query, arrayOf( snode.toString(), publicKey, namespace.toString() ))
|
||||
}
|
||||
|
||||
override fun clearAllLastMessageHashes() {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(lastMessageHashValueTable2, null, null)
|
||||
}
|
||||
|
||||
override fun getReceivedMessageHashValues(publicKey: String, namespace: Int): Set<String>? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
val query = "${Companion.publicKey} = ? AND ${Companion.receivedMessageHashNamespace} = ?"
|
||||
@@ -321,6 +326,11 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
database.insertOrUpdate(receivedMessageHashValuesTable, row, query, arrayOf( publicKey, namespace.toString() ))
|
||||
}
|
||||
|
||||
override fun clearReceivedMessageHashValues() {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(receivedMessageHashValuesTable, null, null)
|
||||
}
|
||||
|
||||
override fun getAuthToken(server: String): String? {
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(openGroupAuthTokenTable, "${Companion.server} = ?", wrap(server)) { cursor ->
|
||||
@@ -339,7 +349,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}
|
||||
|
||||
override fun getLastMessageServerID(room: String, server: String): Long? {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val database = databaseHelper.readableDatabase
|
||||
val index = "$server.$room"
|
||||
return database.get(lastMessageServerIDTable, "$lastMessageServerIDTableIndex = ?", wrap(index)) { cursor ->
|
||||
cursor.getInt(lastMessageServerID)
|
||||
@@ -510,7 +520,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}
|
||||
|
||||
fun getServerCapabilities(serverName: String): List<String> {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(serverCapabilitiesTable, "$server = ?", wrap(serverName)) { cursor ->
|
||||
cursor.getString(capabilities)
|
||||
}?.split(",") ?: emptyList()
|
||||
@@ -523,7 +533,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}
|
||||
|
||||
fun getLastInboxMessageId(serverName: String): Long? {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(lastInboxMessageServerIdTable, "$server = ?", wrap(serverName)) { cursor ->
|
||||
cursor.getInt(lastInboxMessageServerId)
|
||||
}?.toLong()
|
||||
@@ -540,7 +550,7 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
|
||||
}
|
||||
|
||||
fun getLastOutboxMessageId(serverName: String): Long? {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val database = databaseHelper.readableDatabase
|
||||
return database.get(lastOutboxMessageServerIdTable, "$server = ?", wrap(serverName)) { cursor ->
|
||||
cursor.getInt(lastOutboxMessageServerId)
|
||||
}?.toLong()
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import net.sqlcipher.database.SQLiteDatabase.CONFLICT_REPLACE
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase.CONFLICT_REPLACE
|
||||
import org.session.libsignal.database.LokiMessageDatabaseProtocol
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
|
||||
@@ -77,6 +77,25 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
database.endTransaction()
|
||||
}
|
||||
|
||||
fun deleteMessages(messageIDs: List<Long>) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.beginTransaction()
|
||||
|
||||
database.delete(
|
||||
messageIDTable,
|
||||
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||
messageIDs.map { "$it" }.toTypedArray()
|
||||
)
|
||||
database.delete(
|
||||
messageThreadMappingTable,
|
||||
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||
messageIDs.map { "$it" }.toTypedArray()
|
||||
)
|
||||
|
||||
database.setTransactionSuccessful()
|
||||
database.endTransaction()
|
||||
}
|
||||
|
||||
/**
|
||||
* @return pair of sms or mms table-specific ID and whether it is in SMS table
|
||||
*/
|
||||
@@ -96,6 +115,37 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessageIDs(serverIDs: List<Long>, threadID: Long): Pair<List<Long>, List<Long>> {
|
||||
val database = databaseHelper.readableDatabase
|
||||
|
||||
// Retrieve the message ids
|
||||
val messageIdCursor = database
|
||||
.rawQuery(
|
||||
"""
|
||||
SELECT ${messageThreadMappingTable}.${messageID}, ${messageIDTable}.${messageType}
|
||||
FROM ${messageThreadMappingTable}
|
||||
JOIN ${messageIDTable} ON ${messageIDTable}.message_id = ${messageThreadMappingTable}.${messageID}
|
||||
WHERE (
|
||||
${messageThreadMappingTable}.${Companion.threadID} = $threadID AND
|
||||
${messageThreadMappingTable}.${Companion.serverID} IN (${serverIDs.joinToString(",")})
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
val smsMessageIds: MutableList<Long> = mutableListOf()
|
||||
val mmsMessageIds: MutableList<Long> = mutableListOf()
|
||||
while (messageIdCursor.moveToNext()) {
|
||||
if (messageIdCursor.getInt(1) == SMS_TYPE) {
|
||||
smsMessageIds.add(messageIdCursor.getLong(0))
|
||||
}
|
||||
else {
|
||||
mmsMessageIds.add(messageIdCursor.getLong(0))
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(smsMessageIds, mmsMessageIds)
|
||||
}
|
||||
|
||||
override fun setServerID(messageID: Long, serverID: Long, isSms: Boolean) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(3)
|
||||
@@ -136,6 +186,11 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
database.insertOrUpdate(errorMessageTable, contentValues, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
||||
}
|
||||
|
||||
fun clearErrorMessage(messageID: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(errorMessageTable, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
||||
}
|
||||
|
||||
fun deleteThread(threadId: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
try {
|
||||
@@ -178,6 +233,15 @@ class LokiMessageDatabase(context: Context, helper: SQLCipherOpenHelper) : Datab
|
||||
database.delete(messageHashTable, "${Companion.messageID} = ?", arrayOf(messageID.toString()))
|
||||
}
|
||||
|
||||
fun deleteMessageServerHashes(messageIDs: List<Long>) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
database.delete(
|
||||
messageHashTable,
|
||||
"${Companion.messageID} IN (${messageIDs.map { "?" }.joinToString(",")})",
|
||||
messageIDs.map { "$it" }.toTypedArray()
|
||||
)
|
||||
}
|
||||
|
||||
fun migrateThreadId(legacyThreadId: Long, newThreadId: Long) {
|
||||
val database = databaseHelper.writableDatabase
|
||||
val contentValues = ContentValues(1)
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment;
|
||||
import org.session.libsession.utilities.Address;
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.Document;
|
||||
@@ -42,6 +42,7 @@ public abstract class MessagingDatabase extends Database implements MmsSmsColumn
|
||||
public abstract void markAsDeleted(long messageId, boolean read);
|
||||
|
||||
public abstract boolean deleteMessage(long messageId);
|
||||
public abstract boolean deleteMessages(long[] messageId, long threadId);
|
||||
|
||||
public abstract void updateThreadId(long fromId, long toId);
|
||||
|
||||
|
||||
@@ -995,6 +995,23 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
|
||||
return threadDeleted
|
||||
}
|
||||
|
||||
override fun deleteMessages(messageIds: LongArray, threadId: Long): Boolean {
|
||||
val attachmentDatabase = get(context).attachmentDatabase()
|
||||
val groupReceiptDatabase = get(context).groupReceiptDatabase()
|
||||
|
||||
queue(Runnable { attachmentDatabase.deleteAttachmentsForMessages(messageIds) })
|
||||
groupReceiptDatabase.deleteRowsForMessages(messageIds)
|
||||
|
||||
val database = databaseHelper.writableDatabase
|
||||
database!!.delete(TABLE_NAME, ID_IN, arrayOf(messageIds.joinToString(",")))
|
||||
|
||||
val threadDeleted = get(context).threadDatabase().update(threadId, false)
|
||||
notifyConversationListeners(threadId)
|
||||
notifyStickerListeners()
|
||||
notifyStickerPackListeners()
|
||||
return threadDeleted
|
||||
}
|
||||
|
||||
override fun updateThreadId(fromId: Long, toId: Long) {
|
||||
val contentValues = ContentValues(1)
|
||||
contentValues.put(THREAD_ID, toId)
|
||||
|
||||
@@ -22,8 +22,8 @@ import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteQueryBuilder;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteQueryBuilder;
|
||||
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.Util;
|
||||
@@ -112,6 +112,64 @@ public class MmsSmsDatabase extends Database {
|
||||
return getMessageFor(timestamp, author.serialize());
|
||||
}
|
||||
|
||||
public long getPreviousPage(long threadId, long fromTime, int limit) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT+" ASC";
|
||||
String selection = MmsSmsColumns.THREAD_ID+" = "+threadId
|
||||
+ " AND "+MmsSmsColumns.NORMALIZED_DATE_SENT+" > "+fromTime;
|
||||
String limitStr = ""+limit;
|
||||
long sent = -1;
|
||||
Cursor cursor = queryTables(PROJECTION, selection, order, limitStr);
|
||||
if (cursor == null) return sent;
|
||||
Reader reader = readerFor(cursor);
|
||||
if (!cursor.move(limit)) {
|
||||
cursor.moveToLast();
|
||||
}
|
||||
MessageRecord record = reader.getCurrent();
|
||||
sent = record.getDateSent();
|
||||
reader.close();
|
||||
return sent;
|
||||
}
|
||||
|
||||
public Cursor getConversationPage(long threadId, long fromTime, long toTime, int limit) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT+" DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = "+threadId
|
||||
+ " AND "+MmsSmsColumns.NORMALIZED_DATE_SENT+" <= " + fromTime;
|
||||
String limitStr = null;
|
||||
if (toTime != -1L) {
|
||||
selection += " AND "+MmsSmsColumns.NORMALIZED_DATE_SENT+" > "+toTime;
|
||||
} else {
|
||||
limitStr = ""+limit;
|
||||
}
|
||||
|
||||
return queryTables(PROJECTION, selection, order, limitStr);
|
||||
}
|
||||
|
||||
public boolean hasNextPage(long threadId, long toTime) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT+" DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = "+threadId
|
||||
+ " AND "+MmsSmsColumns.NORMALIZED_DATE_SENT+" < " + toTime; // check if there's at least one message before the `toTime`
|
||||
Cursor cursor = queryTables(PROJECTION, selection, order, null);
|
||||
boolean hasNext = false;
|
||||
if (cursor != null) {
|
||||
hasNext = cursor.getCount() > 0;
|
||||
cursor.close();
|
||||
}
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
public boolean hasPreviousPage(long threadId, long fromTime) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT+" DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = "+threadId
|
||||
+ " AND "+MmsSmsColumns.NORMALIZED_DATE_SENT+" > " + fromTime; // check if there's at least one message after the `fromTime`
|
||||
Cursor cursor = queryTables(PROJECTION, selection, order, null);
|
||||
boolean hasNext = false;
|
||||
if (cursor != null) {
|
||||
hasNext = cursor.getCount() > 0;
|
||||
cursor.close();
|
||||
}
|
||||
return hasNext;
|
||||
}
|
||||
|
||||
public Cursor getConversation(long threadId, boolean reverse, long offset, long limit) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + (reverse ? " DESC" : " ASC");
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
||||
@@ -199,16 +257,16 @@ public class MmsSmsDatabase extends Database {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getMessagePositionInConversation(long threadId, long receivedTimestamp, @NonNull Address address) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC";
|
||||
public int getMessagePositionInConversation(long threadId, long sentTimestamp, @NonNull Address address) {
|
||||
String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC";
|
||||
String selection = MmsSmsColumns.THREAD_ID + " = " + threadId;
|
||||
|
||||
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_RECEIVED, MmsSmsColumns.ADDRESS }, selection, order, null)) {
|
||||
try (Cursor cursor = queryTables(new String[]{ MmsSmsColumns.NORMALIZED_DATE_SENT, MmsSmsColumns.ADDRESS }, selection, order, null)) {
|
||||
String serializedAddress = address.serialize();
|
||||
boolean isOwnNumber = Util.isOwnNumber(context, address.serialize());
|
||||
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
boolean timestampMatches = cursor.getLong(0) == receivedTimestamp;
|
||||
boolean timestampMatches = cursor.getLong(0) == sentTimestamp;
|
||||
boolean addressMatches = serializedAddress.equals(cursor.getString(1));
|
||||
|
||||
if (timestampMatches && (addressMatches || isOwnNumber)) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.database.Cursor;
|
||||
import androidx.annotation.NonNull;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
import org.session.libsignal.utilities.Base64;
|
||||
|
||||
@@ -48,6 +48,14 @@ class ReactionDatabase(context: Context, helper: SQLCipherOpenHelper) : Database
|
||||
)
|
||||
""".trimIndent()
|
||||
|
||||
@JvmField
|
||||
val CREATE_INDEXS = arrayOf(
|
||||
"CREATE INDEX IF NOT EXISTS reaction_message_id_index ON " + ReactionDatabase.TABLE_NAME + " (" + ReactionDatabase.MESSAGE_ID + ");",
|
||||
"CREATE INDEX IF NOT EXISTS reaction_is_mms_index ON " + ReactionDatabase.TABLE_NAME + " (" + ReactionDatabase.IS_MMS + ");",
|
||||
"CREATE INDEX IF NOT EXISTS reaction_message_id_is_mms_index ON " + ReactionDatabase.TABLE_NAME + " (" + ReactionDatabase.MESSAGE_ID + ", " + ReactionDatabase.IS_MMS + ");",
|
||||
"CREATE INDEX IF NOT EXISTS reaction_sort_id_index ON " + ReactionDatabase.TABLE_NAME + " (" + ReactionDatabase.SORT_ID + ");",
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val CREATE_REACTION_TRIGGERS = arrayOf(
|
||||
"""
|
||||
|
||||
@@ -11,7 +11,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.session.libsession.utilities.MaterialColor;
|
||||
@@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class RecipientDatabase extends Database {
|
||||
@@ -232,6 +233,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(COLOR, color.serialize());
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setColor(color);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setDefaultSubscriptionId(@NonNull Recipient recipient, int defaultSubscriptionId) {
|
||||
@@ -239,6 +241,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(DEFAULT_SUBSCRIPTION_ID, defaultSubscriptionId);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setDefaultSubscriptionId(Optional.of(defaultSubscriptionId));
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setForceSmsSelection(@NonNull Recipient recipient, boolean forceSmsSelection) {
|
||||
@@ -246,6 +249,7 @@ public class RecipientDatabase extends Database {
|
||||
contentValues.put(FORCE_SMS_SELECTION, forceSmsSelection ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.resolve().setForceSmsSelection(forceSmsSelection);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setApproved(@NonNull Recipient recipient, boolean approved) {
|
||||
@@ -253,6 +257,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(APPROVED, approved ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setApproved(approved);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setApprovedMe(@NonNull Recipient recipient, boolean approvedMe) {
|
||||
@@ -260,6 +265,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(APPROVED_ME, approvedMe ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setHasApprovedMe(approvedMe);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setBlocked(@NonNull Recipient recipient, boolean blocked) {
|
||||
@@ -267,6 +273,24 @@ public class RecipientDatabase extends Database {
|
||||
values.put(BLOCK, blocked ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setBlocked(blocked);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setBlocked(@NonNull List<Recipient> recipients, boolean blocked) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.beginTransaction();
|
||||
try {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(BLOCK, blocked ? 1 : 0);
|
||||
for (Recipient recipient : recipients) {
|
||||
db.update(TABLE_NAME, values, ADDRESS + " = ?", new String[]{recipient.getAddress().serialize()});
|
||||
recipient.resolve().setBlocked(blocked);
|
||||
}
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setMuted(@NonNull Recipient recipient, long until) {
|
||||
@@ -274,6 +298,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(MUTE_UNTIL, until);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setMuted(until);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -287,6 +312,7 @@ public class RecipientDatabase extends Database {
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setNotifyType(notifyType);
|
||||
notifyConversationListListeners();
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setExpireMessages(@NonNull Recipient recipient, int expiration) {
|
||||
@@ -296,6 +322,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(EXPIRE_MESSAGES, expiration);
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setExpireMessages(expiration);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
|
||||
@@ -303,6 +330,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
|
||||
@@ -310,6 +338,7 @@ public class RecipientDatabase extends Database {
|
||||
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
|
||||
updateOrInsert(recipient.getAddress(), values);
|
||||
recipient.resolve().setProfileKey(profileKey);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setProfileAvatar(@NonNull Recipient recipient, @Nullable String profileAvatar) {
|
||||
@@ -317,6 +346,7 @@ public class RecipientDatabase extends Database {
|
||||
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.resolve().setProfileAvatar(profileAvatar);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setProfileName(@NonNull Recipient recipient, @Nullable String profileName) {
|
||||
@@ -325,6 +355,7 @@ public class RecipientDatabase extends Database {
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.resolve().setName(profileName);
|
||||
recipient.resolve().setProfileName(profileName);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setProfileSharing(@NonNull Recipient recipient, boolean enabled) {
|
||||
@@ -332,6 +363,7 @@ public class RecipientDatabase extends Database {
|
||||
contentValues.put(PROFILE_SHARING, enabled ? 1 : 0);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.setProfileSharing(enabled);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setNotificationChannel(@NonNull Recipient recipient, @Nullable String notificationChannel) {
|
||||
@@ -339,6 +371,7 @@ public class RecipientDatabase extends Database {
|
||||
contentValues.put(NOTIFICATION_CHANNEL, notificationChannel);
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.setNotificationChannel(notificationChannel);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
public void setRegistered(@NonNull Recipient recipient, RegisteredState registeredState) {
|
||||
@@ -346,6 +379,7 @@ public class RecipientDatabase extends Database {
|
||||
contentValues.put(REGISTERED, registeredState.getId());
|
||||
updateOrInsert(recipient.getAddress(), contentValues);
|
||||
recipient.setRegistered(registeredState);
|
||||
notifyRecipientListeners();
|
||||
}
|
||||
|
||||
private void updateOrInsert(Address address, ContentValues contentValues) {
|
||||
@@ -365,6 +399,22 @@ public class RecipientDatabase extends Database {
|
||||
database.endTransaction();
|
||||
}
|
||||
|
||||
public List<Recipient> getBlockedContacts() {
|
||||
SQLiteDatabase database = databaseHelper.getReadableDatabase();
|
||||
|
||||
Cursor cursor = database.query(TABLE_NAME, new String[] {ID, ADDRESS}, BLOCK + " = 1",
|
||||
null, null, null, null, null);
|
||||
|
||||
RecipientReader reader = new RecipientReader(context, cursor);
|
||||
List<Recipient> returnList = new ArrayList<>();
|
||||
Recipient current;
|
||||
while ((current = reader.getNext()) != null) {
|
||||
returnList.add(current);
|
||||
}
|
||||
reader.close();
|
||||
return returnList;
|
||||
}
|
||||
|
||||
public static class RecipientReader implements Closeable {
|
||||
|
||||
private final Context context;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.Cursor;
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.session.libsession.utilities.Util;
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
|
||||
@@ -63,7 +63,7 @@ public class SearchDatabase extends Database {
|
||||
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
|
||||
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
|
||||
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
|
||||
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
|
||||
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT + ", " +
|
||||
SMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
|
||||
"FROM " + SmsDatabase.TABLE_NAME + " " +
|
||||
"INNER JOIN " + SMS_FTS_TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + ID + " = " + SmsDatabase.TABLE_NAME + "." + SmsDatabase.ID + " " +
|
||||
@@ -74,13 +74,13 @@ public class SearchDatabase extends Database {
|
||||
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
|
||||
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
|
||||
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
|
||||
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
|
||||
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT + ", " +
|
||||
MMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
|
||||
"FROM " + MmsDatabase.TABLE_NAME + " " +
|
||||
"INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " " +
|
||||
"INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " +
|
||||
"WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? " +
|
||||
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC " +
|
||||
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC " +
|
||||
"LIMIT ?";
|
||||
|
||||
private static final String MESSAGES_FOR_THREAD_QUERY =
|
||||
@@ -88,7 +88,7 @@ public class SearchDatabase extends Database {
|
||||
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
|
||||
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
|
||||
"snippet(" + SMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
|
||||
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
|
||||
SmsDatabase.TABLE_NAME + "." + SmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT + ", " +
|
||||
SMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
|
||||
"FROM " + SmsDatabase.TABLE_NAME + " " +
|
||||
"INNER JOIN " + SMS_FTS_TABLE_NAME + " ON " + SMS_FTS_TABLE_NAME + "." + ID + " = " + SmsDatabase.TABLE_NAME + "." + SmsDatabase.ID + " " +
|
||||
@@ -99,13 +99,13 @@ public class SearchDatabase extends Database {
|
||||
ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ADDRESS + " AS " + CONVERSATION_ADDRESS + ", " +
|
||||
MmsSmsColumns.ADDRESS + " AS " + MESSAGE_ADDRESS + ", " +
|
||||
"snippet(" + MMS_FTS_TABLE_NAME + ", -1, '', '', '...', 7) AS " + SNIPPET + ", " +
|
||||
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_RECEIVED + " AS " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + ", " +
|
||||
MmsDatabase.TABLE_NAME + "." + MmsDatabase.DATE_SENT + " AS " + MmsSmsColumns.NORMALIZED_DATE_SENT + ", " +
|
||||
MMS_FTS_TABLE_NAME + "." + THREAD_ID + " " +
|
||||
"FROM " + MmsDatabase.TABLE_NAME + " " +
|
||||
"INNER JOIN " + MMS_FTS_TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + ID + " = " + MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID + " " +
|
||||
"INNER JOIN " + ThreadDatabase.TABLE_NAME + " ON " + MMS_FTS_TABLE_NAME + "." + THREAD_ID + " = " + ThreadDatabase.TABLE_NAME + "." + ThreadDatabase.ID + " " +
|
||||
"WHERE " + MMS_FTS_TABLE_NAME + " MATCH ? AND " + MmsDatabase.TABLE_NAME + "." + MmsSmsColumns.THREAD_ID + " = ? " +
|
||||
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_RECEIVED + " DESC " +
|
||||
"ORDER BY " + MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC " +
|
||||
"LIMIT 500";
|
||||
|
||||
public SearchDatabase(@NonNull Context context, @NonNull SQLCipherOpenHelper databaseHelper) {
|
||||
|
||||
@@ -3,7 +3,7 @@ package org.thoughtcrime.securesms.database
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import androidx.core.database.getStringOrNull
|
||||
import net.sqlcipher.Cursor
|
||||
import android.database.Cursor
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsignal.utilities.Base64
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
@@ -75,21 +75,6 @@ class SessionContactDatabase(context: Context, helper: SQLCipherOpenHelper) : Da
|
||||
}
|
||||
|
||||
fun contactFromCursor(cursor: Cursor): Contact {
|
||||
val sessionID = cursor.getString(sessionID)
|
||||
val contact = Contact(sessionID)
|
||||
contact.name = cursor.getStringOrNull(name)
|
||||
contact.nickname = cursor.getStringOrNull(nickname)
|
||||
contact.profilePictureURL = cursor.getStringOrNull(profilePictureURL)
|
||||
contact.profilePictureFileName = cursor.getStringOrNull(profilePictureFileName)
|
||||
cursor.getStringOrNull(profilePictureEncryptionKey)?.let {
|
||||
contact.profilePictureEncryptionKey = Base64.decode(it)
|
||||
}
|
||||
contact.threadID = cursor.getLong(threadID)
|
||||
contact.isTrusted = cursor.getInt(isTrusted) != 0
|
||||
return contact
|
||||
}
|
||||
|
||||
fun contactFromCursor(cursor: android.database.Cursor): Contact {
|
||||
val sessionID = cursor.getString(cursor.getColumnIndexOrThrow(sessionID))
|
||||
val contact = Contact(sessionID)
|
||||
contact.name = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(name))
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import net.sqlcipher.Cursor
|
||||
import android.database.Cursor
|
||||
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
||||
import org.session.libsession.messaging.jobs.BackgroundGroupAddJob
|
||||
import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob
|
||||
|
||||
@@ -28,9 +28,10 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteStatement;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteStatement;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.session.libsession.messaging.calls.CallMessageType;
|
||||
import org.session.libsession.messaging.messages.signal.IncomingGroupMessage;
|
||||
import org.session.libsession.messaging.messages.signal.IncomingTextMessage;
|
||||
@@ -52,6 +53,7 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -596,6 +598,30 @@ public class SmsDatabase extends MessagingDatabase {
|
||||
return threadDeleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteMessages(long[] messageIds, long threadId) {
|
||||
String[] argsArray = new String[messageIds.length];
|
||||
String[] argValues = new String[messageIds.length];
|
||||
Arrays.fill(argsArray, "?");
|
||||
|
||||
for (int i = 0; i < messageIds.length; i++) {
|
||||
argValues[i] = (messageIds[i] + "");
|
||||
}
|
||||
|
||||
String combinedMessageIdArgss = StringUtils.join(messageIds, ',');
|
||||
String combinedMessageIds = StringUtils.join(messageIds, ',');
|
||||
Log.i("MessageDatabase", "Deleting: " + combinedMessageIds);
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
db.delete(
|
||||
TABLE_NAME,
|
||||
ID + " IN (" + StringUtils.join(argsArray, ',') + ")",
|
||||
argValues
|
||||
);
|
||||
boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false);
|
||||
notifyConversationListeners(threadId);
|
||||
return threadDeleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateThreadId(long fromId, long toId) {
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
|
||||
@@ -7,28 +7,18 @@ import org.session.libsession.database.StorageProtocol
|
||||
import org.session.libsession.messaging.BlindedIdMapping
|
||||
import org.session.libsession.messaging.calls.CallMessageType
|
||||
import org.session.libsession.messaging.contacts.Contact
|
||||
import org.session.libsession.messaging.jobs.AttachmentUploadJob
|
||||
import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob
|
||||
import org.session.libsession.messaging.jobs.Job
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.jobs.MessageReceiveJob
|
||||
import org.session.libsession.messaging.jobs.MessageSendJob
|
||||
import org.session.libsession.messaging.jobs.*
|
||||
import org.session.libsession.messaging.messages.Message
|
||||
import org.session.libsession.messaging.messages.control.ConfigurationMessage
|
||||
import org.session.libsession.messaging.messages.control.MessageRequestResponse
|
||||
import org.session.libsession.messaging.messages.signal.IncomingEncryptedMessage
|
||||
import org.session.libsession.messaging.messages.signal.IncomingGroupMessage
|
||||
import org.session.libsession.messaging.messages.signal.IncomingMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.IncomingTextMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage
|
||||
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage
|
||||
import org.session.libsession.messaging.messages.signal.*
|
||||
import org.session.libsession.messaging.messages.visible.Attachment
|
||||
import org.session.libsession.messaging.messages.visible.Profile
|
||||
import org.session.libsession.messaging.messages.visible.Reaction
|
||||
import org.session.libsession.messaging.messages.visible.VisibleMessage
|
||||
import org.session.libsession.messaging.open_groups.GroupMember
|
||||
import org.session.libsession.messaging.open_groups.OpenGroup
|
||||
import org.session.libsession.messaging.open_groups.OpenGroupApi
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
|
||||
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
|
||||
@@ -38,7 +28,7 @@ import org.session.libsession.messaging.utilities.SessionId
|
||||
import org.session.libsession.messaging.utilities.SodiumUtilities
|
||||
import org.session.libsession.messaging.utilities.UpdateMessageData
|
||||
import org.session.libsession.snode.OnionRequestAPI
|
||||
import org.session.libsession.utilities.Address
|
||||
import org.session.libsession.utilities.*
|
||||
import org.session.libsession.utilities.Address.Companion.fromSerialized
|
||||
import org.session.libsession.utilities.GroupRecord
|
||||
import org.session.libsession.utilities.GroupUtil
|
||||
@@ -318,12 +308,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return getAllOpenGroups().values.firstOrNull { it.server == server && it.room == room }
|
||||
}
|
||||
|
||||
override fun clearGroupMemberRoles(groupId: String) {
|
||||
DatabaseComponent.get(context).groupMemberDatabase().clearGroupMemberRoles(groupId)
|
||||
}
|
||||
|
||||
override fun addGroupMemberRole(member: GroupMember) {
|
||||
DatabaseComponent.get(context).groupMemberDatabase().addGroupMember(member)
|
||||
override fun setGroupMemberRoles(members: List<GroupMember>) {
|
||||
DatabaseComponent.get(context).groupMemberDatabase().setGroupMembers(members)
|
||||
}
|
||||
|
||||
override fun isDuplicateMessage(timestamp: Long): Boolean {
|
||||
@@ -338,6 +324,10 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseComponent.get(context).groupDatabase().updateProfilePicture(groupID, newValue)
|
||||
}
|
||||
|
||||
override fun hasDownloadedProfilePicture(groupID: String): Boolean {
|
||||
return DatabaseComponent.get(context).groupDatabase().hasDownloadedProfilePicture(groupID)
|
||||
}
|
||||
|
||||
override fun getReceivedMessageTimestamps(): Set<Long> {
|
||||
return SessionMetaProtocol.getTimestamps()
|
||||
}
|
||||
@@ -431,6 +421,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearErrorMessage(messageID: Long) {
|
||||
val db = DatabaseComponent.get(context).lokiMessageDatabase()
|
||||
db.clearErrorMessage(messageID)
|
||||
}
|
||||
|
||||
override fun setMessageServerHash(messageID: Long, serverHash: String) {
|
||||
DatabaseComponent.get(context).lokiMessageDatabase().setMessageServerHash(messageID, serverHash)
|
||||
}
|
||||
@@ -565,8 +560,8 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
return DatabaseComponent.get(context).groupDatabase().allGroups
|
||||
}
|
||||
|
||||
override fun addOpenGroup(urlAsString: String) {
|
||||
OpenGroupManager.addOpenGroup(urlAsString, context)
|
||||
override fun addOpenGroup(urlAsString: String): OpenGroupApi.RoomInfo? {
|
||||
return OpenGroupManager.addOpenGroup(urlAsString, context)
|
||||
}
|
||||
|
||||
override fun onOpenGroupAdded(server: String) {
|
||||
@@ -673,7 +668,6 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
val threadId = threadDatabase.getOrCreateThreadIdFor(recipient)
|
||||
if (contact.didApproveMe == true) {
|
||||
recipientDatabase.setApprovedMe(recipient, true)
|
||||
threadDatabase.setHasSent(threadId, true)
|
||||
}
|
||||
if (contact.isApproved == true) {
|
||||
recipientDatabase.setApproved(recipient, true)
|
||||
@@ -974,4 +968,14 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
|
||||
DatabaseComponent.get(context).reactionDatabase().deleteMessageReactions(MessageId(messageId, mms))
|
||||
}
|
||||
|
||||
override fun unblock(toUnblock: List<Recipient>) {
|
||||
val recipientDb = DatabaseComponent.get(context).recipientDatabase()
|
||||
recipientDb.setBlocked(toUnblock, false)
|
||||
}
|
||||
|
||||
override fun blockedContacts(): List<Recipient> {
|
||||
val recipientDb = DatabaseComponent.get(context).recipientDatabase()
|
||||
return recipientDb.blockedContacts
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.annimon.stream.Stream;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.session.libsession.utilities.Address;
|
||||
@@ -502,15 +502,23 @@ public class ThreadDatabase extends Database {
|
||||
return db.rawQuery(query, null);
|
||||
}
|
||||
|
||||
public void setLastSeen(long threadId) {
|
||||
public void setLastSeen(long threadId, long timestamp) {
|
||||
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
ContentValues contentValues = new ContentValues(1);
|
||||
contentValues.put(LAST_SEEN, System.currentTimeMillis());
|
||||
if (timestamp == -1) {
|
||||
contentValues.put(LAST_SEEN, System.currentTimeMillis());
|
||||
} else {
|
||||
contentValues.put(LAST_SEEN, timestamp);
|
||||
}
|
||||
|
||||
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(threadId)});
|
||||
notifyConversationListListeners();
|
||||
}
|
||||
|
||||
public void setLastSeen(long threadId) {
|
||||
setLastSeen(threadId, -1);
|
||||
}
|
||||
|
||||
public Pair<Long, Boolean> getLastSeenAndHasSent(long threadId) {
|
||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||
Cursor cursor = db.query(TABLE_NAME, new String[]{LAST_SEEN, HAS_SENT}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package org.thoughtcrime.securesms.database.helpers;
|
||||
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import net.sqlcipher.database.SQLiteDatabase;
|
||||
import net.sqlcipher.database.SQLiteDatabaseHook;
|
||||
import net.sqlcipher.database.SQLiteOpenHelper;
|
||||
import net.zetetic.database.sqlcipher.SQLiteConnection;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabaseHook;
|
||||
import net.zetetic.database.sqlcipher.SQLiteException;
|
||||
import net.zetetic.database.sqlcipher.SQLiteOpenHelper;
|
||||
|
||||
import org.session.libsession.utilities.TextSecurePreferences;
|
||||
import org.session.libsignal.utilities.Log;
|
||||
@@ -35,6 +39,11 @@ import org.thoughtcrime.securesms.database.SessionContactDatabase;
|
||||
import org.thoughtcrime.securesms.database.SessionJobDatabase;
|
||||
import org.thoughtcrime.securesms.database.SmsDatabase;
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||
import org.thoughtcrime.securesms.notifications.NotificationChannels;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import network.loki.messenger.R;
|
||||
|
||||
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
@@ -75,40 +84,157 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
private static final int lokiV36 = 57;
|
||||
private static final int lokiV37 = 58;
|
||||
private static final int lokiV38 = 59;
|
||||
private static final int lokiV39 = 60;
|
||||
|
||||
// Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
|
||||
private static final int DATABASE_VERSION = lokiV38;
|
||||
private static final String DATABASE_NAME = "signal.db";
|
||||
private static final int DATABASE_VERSION = lokiV39;
|
||||
private static final int MIN_DATABASE_VERSION = lokiV7;
|
||||
private static final String CIPHER3_DATABASE_NAME = "signal.db";
|
||||
public static final String DATABASE_NAME = "signal_v4.db";
|
||||
|
||||
private final Context context;
|
||||
private final DatabaseSecret databaseSecret;
|
||||
|
||||
public SQLCipherOpenHelper(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION, new SQLiteDatabaseHook() {
|
||||
super(context, DATABASE_NAME, databaseSecret.asString(), null, DATABASE_VERSION, MIN_DATABASE_VERSION, null, new SQLiteDatabaseHook() {
|
||||
@Override
|
||||
public void preKey(SQLiteDatabase db) {
|
||||
db.rawExecSQL("PRAGMA cipher_default_kdf_iter = 1;");
|
||||
db.rawExecSQL("PRAGMA cipher_default_page_size = 4096;");
|
||||
public void preKey(SQLiteConnection connection) {
|
||||
SQLCipherOpenHelper.applySQLCipherPragmas(connection, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postKey(SQLiteDatabase db) {
|
||||
db.rawExecSQL("PRAGMA kdf_iter = '1';");
|
||||
db.rawExecSQL("PRAGMA cipher_page_size = 4096;");
|
||||
public void postKey(SQLiteConnection connection) {
|
||||
SQLCipherOpenHelper.applySQLCipherPragmas(connection, true);
|
||||
|
||||
// if not vacuumed in a while, perform that operation
|
||||
long currentTime = System.currentTimeMillis();
|
||||
// 7 days
|
||||
if (currentTime - TextSecurePreferences.getLastVacuumTime(context) > 604_800_000) {
|
||||
db.rawExecSQL("VACUUM;");
|
||||
connection.execute("VACUUM;", null, null);
|
||||
TextSecurePreferences.setLastVacuumNow(context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
|
||||
this.context = context.getApplicationContext();
|
||||
this.databaseSecret = databaseSecret;
|
||||
}
|
||||
|
||||
private static void applySQLCipherPragmas(SQLiteConnection connection, boolean useSQLCipher4) {
|
||||
if (useSQLCipher4) {
|
||||
connection.execute("PRAGMA kdf_iter = '256000';", null, null);
|
||||
}
|
||||
else {
|
||||
connection.execute("PRAGMA cipher_compatibility = 3;", null, null);
|
||||
connection.execute("PRAGMA kdf_iter = '1';", null, null);
|
||||
}
|
||||
|
||||
connection.execute("PRAGMA cipher_page_size = 4096;", null, null);
|
||||
}
|
||||
|
||||
private static SQLiteDatabase open(String path, DatabaseSecret databaseSecret, boolean useSQLCipher4) throws SQLiteException {
|
||||
return SQLiteDatabase.openDatabase(path, databaseSecret.asString(), null, SQLiteDatabase.OPEN_READWRITE, new SQLiteDatabaseHook() {
|
||||
@Override
|
||||
public void preKey(SQLiteConnection connection) { SQLCipherOpenHelper.applySQLCipherPragmas(connection, useSQLCipher4); }
|
||||
|
||||
@Override
|
||||
public void postKey(SQLiteConnection connection) { SQLCipherOpenHelper.applySQLCipherPragmas(connection, useSQLCipher4); }
|
||||
});
|
||||
}
|
||||
|
||||
public static void migrateSqlCipher3To4IfNeeded(@NonNull Context context, @NonNull DatabaseSecret databaseSecret) throws Exception {
|
||||
String oldDbPath = context.getDatabasePath(CIPHER3_DATABASE_NAME).getPath();
|
||||
File oldDbFile = new File(oldDbPath);
|
||||
|
||||
// If the old SQLCipher3 database file doesn't exist then no need to do anything
|
||||
if (!oldDbFile.exists()) { return; }
|
||||
|
||||
try {
|
||||
// Define the location for the new database
|
||||
String newDbPath = context.getDatabasePath(DATABASE_NAME).getPath();
|
||||
File newDbFile = new File(newDbPath);
|
||||
|
||||
// If the new database file already exists then check if it's valid first, if it's in an
|
||||
// invalid state we should delete it and try to migrate again
|
||||
if (newDbFile.exists()) {
|
||||
// If the old database hasn't been modified since the new database was created, then we can
|
||||
// assume the user hasn't downgraded for some reason and made changes to the old database and
|
||||
// can remove the old database file (it won't be used anymore)
|
||||
if (oldDbFile.lastModified() <= newDbFile.lastModified()) {
|
||||
// TODO: Delete 'CIPHER3_DATABASE_NAME' once enough time has past
|
||||
// //noinspection ResultOfMethodCallIgnored
|
||||
// oldDbFile.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the old database does have newer changes then the new database could have stale/invalid
|
||||
// data and we should re-migrate to avoid losing any data or issues
|
||||
if (!newDbFile.delete()) {
|
||||
throw new Exception("Failed to remove invalid new database");
|
||||
}
|
||||
}
|
||||
|
||||
if (!newDbFile.createNewFile()) {
|
||||
throw new Exception("Failed to create new database");
|
||||
}
|
||||
|
||||
// Open the old database and extract it's version
|
||||
SQLiteDatabase oldDb = SQLCipherOpenHelper.open(oldDbPath, databaseSecret, false);
|
||||
int oldDbVersion = oldDb.getVersion();
|
||||
|
||||
// Export the old database to the new one (will have the default 'kdf_iter' and 'page_size' settings)
|
||||
oldDb.rawExecSQL(
|
||||
String.format("ATTACH DATABASE '%s' AS sqlcipher4 KEY '%s'", newDbPath, databaseSecret.asString())
|
||||
);
|
||||
Cursor cursor = oldDb.rawQuery("SELECT sqlcipher_export('sqlcipher4')");
|
||||
cursor.moveToLast();
|
||||
cursor.close();
|
||||
oldDb.rawExecSQL("DETACH DATABASE sqlcipher4");
|
||||
oldDb.close();
|
||||
|
||||
// Open the newly migrated database (to ensure it works) and set it's version so we don't try
|
||||
// to run any of our custom migrations
|
||||
SQLiteDatabase newDb = SQLCipherOpenHelper.open(newDbPath, databaseSecret, true);
|
||||
newDb.setVersion(oldDbVersion);
|
||||
newDb.close();
|
||||
|
||||
// TODO: Delete 'CIPHER3_DATABASE_NAME' once enough time has past
|
||||
// Remove the old database file since it will no longer be used
|
||||
// //noinspection ResultOfMethodCallIgnored
|
||||
// oldDbFile.delete();
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.e(TAG, "Migration from SQLCipher3 to SQLCipher4 failed", e);
|
||||
|
||||
// Notify the user of the issue so they know they can downgrade until the issue is fixed
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
String channelId = context.getString(R.string.NotificationChannel_failures);
|
||||
|
||||
if (NotificationChannels.supported()) {
|
||||
NotificationChannel channel = new NotificationChannel(channelId, channelId, NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.enableVibration(true);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
}
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setColor(context.getResources().getColor(R.color.textsecure_primary))
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
.setContentTitle(context.getString(R.string.ErrorNotifier_migration))
|
||||
.setContentText(context.getString(R.string.ErrorNotifier_migration_downgrade))
|
||||
.setAutoCancel(true);
|
||||
|
||||
if (!NotificationChannels.supported()) {
|
||||
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
|
||||
}
|
||||
|
||||
notificationManager.notify(5874, builder.build());
|
||||
|
||||
// Throw the error (app will crash but there is nothing else we can do unfortunately)
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(SmsDatabase.CREATE_TABLE);
|
||||
@@ -188,6 +314,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
executeStatements(db, DraftDatabase.CREATE_INDEXS);
|
||||
executeStatements(db, GroupDatabase.CREATE_INDEXS);
|
||||
executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES);
|
||||
executeStatements(db, ReactionDatabase.CREATE_INDEXS);
|
||||
|
||||
executeStatements(db, ReactionDatabase.CREATE_REACTION_TRIGGERS);
|
||||
}
|
||||
@@ -195,9 +322,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
@Override
|
||||
public void onConfigure(SQLiteDatabase db) {
|
||||
super.onConfigure(db);
|
||||
// Loki - Enable write ahead logging mode and increase the cache size.
|
||||
// This should be disabled if we ever run into serious race condition bugs.
|
||||
db.enableWriteAheadLogging();
|
||||
|
||||
db.execSQL("PRAGMA cache_size = 10000");
|
||||
}
|
||||
|
||||
@@ -414,20 +539,16 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
|
||||
db.execSQL(EmojiSearchDatabase.CREATE_EMOJI_SEARCH_TABLE_COMMAND);
|
||||
}
|
||||
|
||||
if (oldVersion < lokiV39) {
|
||||
executeStatements(db, ReactionDatabase.CREATE_INDEXS);
|
||||
}
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public SQLiteDatabase getReadableDatabase() {
|
||||
return getReadableDatabase(databaseSecret.asString());
|
||||
}
|
||||
|
||||
public SQLiteDatabase getWritableDatabase() {
|
||||
return getWritableDatabase(databaseSecret.asString());
|
||||
}
|
||||
|
||||
public void markCurrent(SQLiteDatabase db) {
|
||||
db.setVersion(DATABASE_VERSION);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.session.libsession.utilities.NetworkFailure;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The base class for message record models that are displayed in
|
||||
@@ -140,14 +141,16 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||
return spannable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof MessageRecord
|
||||
&& ((MessageRecord) other).getId() == getId()
|
||||
&& ((MessageRecord) other).isMms() == isMms();
|
||||
&& ((MessageRecord) other).getId() == getId()
|
||||
&& ((MessageRecord) other).isMms() == isMms();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (int)getId();
|
||||
return Objects.hash(id, isMms());
|
||||
}
|
||||
|
||||
public @NonNull List<ReactionRecord> getReactions() {
|
||||
|
||||
@@ -2,13 +2,15 @@ package org.thoughtcrime.securesms.database.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import org.session.libsession.utilities.Contact;
|
||||
|
||||
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.session.libsession.utilities.Contact;
|
||||
import org.session.libsession.utilities.IdentityKeyMismatch;
|
||||
import org.session.libsession.utilities.NetworkFailure;
|
||||
import org.session.libsession.utilities.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.mms.Slide;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
|
||||
import org.session.libsession.utilities.Address;
|
||||
import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Quote {
|
||||
|
||||
private final long id;
|
||||
@@ -47,4 +49,17 @@ public class Quote {
|
||||
public QuoteModel getQuoteModel() {
|
||||
return new QuoteModel(id, author, text, missing, attachment.asAttachments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Quote quote = (Quote) o;
|
||||
return id == quote.id && missing == quote.missing && Objects.equals(author, quote.author) && Objects.equals(text, quote.text) && Objects.equals(attachment, quote.attachment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, author, text, missing, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class ThreadRecord extends DisplayRecord {
|
||||
this.archived = archived;
|
||||
this.expiresIn = expiresIn;
|
||||
this.lastSeen = lastSeen;
|
||||
this.pinned = pinned;
|
||||
this.pinned = pinned;
|
||||
}
|
||||
|
||||
public @Nullable Uri getSnippetUri() {
|
||||
|
||||
Reference in New Issue
Block a user